CSS dan gaya
Artikel ini membahas lebih lanjut hal-hal luar biasa yang dapat Anda lakukan dengan Shadow DOM. Cara ini dibuat berdasarkan konsep yang dibahas dalam Shadow DOM 101. Jika Anda mencari pengantar, lihat artikel tersebut.
Pengantar
Mari kita hadapi. Tidak ada yang menarik dari markup tanpa gaya. Untungnya, orang-orang hebat di balik Web Components telah mengantisipasi hal ini dan tidak membiarkan kita menunggu. Modul Cakupan CSS menentukan banyak opsi untuk menata gaya konten dalam hierarki bayangan.
Enkapsulasi gaya
Salah satu fitur inti Shadow DOM adalah batas bayangan. Library ini memiliki banyak properti bagus, tetapi salah satu yang terbaik adalah menyediakan enkapsulasi gaya secara gratis. Dengan kata lain:
<div><h3>Light DOM</h3></div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.innerHTML = `
<style>
h3 {
color: red;
}
</style>
<h3>Shadow DOM</h3>
`;
</script>
Ada dua pengamatan menarik tentang demo ini:
- Ada h3 lain di halaman ini, tetapi satu-satunya yang cocok dengan pemilih h3, dan karenanya bergaya merah, adalah yang ada di ShadowRoot. Sekali lagi, gaya cakupan secara default.
- Aturan gaya lain yang ditentukan di halaman ini yang menargetkan h3 tidak masuk ke konten saya. Hal ini karena pemilih tidak melintasi batas bayangan.
Pesan moralnya? Kita memiliki enkapsulasi gaya dari dunia luar. Terima kasih, Shadow DOM!
Menyesuaikan gaya elemen host
:host
memungkinkan Anda memilih dan menata gaya elemen yang menghosting hierarki bayangan:
<button class="red">My Button</button>
<script>
var button = document.querySelector('button');
var root = button.createShadowRoot();
root.innerHTML = `
<style>
:host {
text-transform: uppercase;
}
</style>
<content></content>
`;
</script>
Satu hal yang perlu diperhatikan adalah aturan di halaman induk memiliki spesifitas yang lebih tinggi daripada aturan :host
yang ditentukan dalam elemen, tetapi spesifitas yang lebih rendah daripada atribut style
yang ditentukan pada elemen host. Hal ini memungkinkan pengguna mengganti gaya visual Anda dari luar.
:host
juga hanya berfungsi dalam konteks ShadowRoot sehingga Anda tidak dapat menggunakannya di luar Shadow DOM.
Bentuk fungsional :host(<selector>)
memungkinkan Anda menargetkan elemen host jika cocok dengan <selector>
.
Contoh - hanya cocok jika elemen itu sendiri memiliki class .different
(misalnya <x-foo class="different"></x-foo>
):
:host(.different) {
...
}
Merespons status pengguna
Kasus penggunaan umum untuk :host
adalah saat Anda membuat Elemen Kustom dan ingin bereaksi terhadap berbagai status pengguna (:hover, :focus, :active, dll.).
<style>
:host {
opacity: 0.4;
transition: opacity 420ms ease-in-out;
}
:host(:hover) {
opacity: 1;
}
:host(:active) {
position: relative;
top: 3px;
left: 3px;
}
</style>
Menerapkan tema pada elemen
Class pseudo :host-context(<selector>)
cocok dengan elemen host jika elemen tersebut atau salah satu ancestor-nya cocok dengan <selector>
.
Penggunaan umum :host-context()
adalah untuk tema elemen berdasarkan lingkungannya. Misalnya,
banyak orang melakukan tema dengan menerapkan class ke <html>
atau <body>
:
<body class="different">
<x-foo></x-foo>
</body>
Anda dapat :host-context(.different)
untuk menata gaya <x-foo>
jika merupakan turunan elemen dengan class .different
:
:host-context(.different) {
color: red;
}
Hal ini memberi Anda kemampuan untuk mengenkapsulasi aturan gaya di DOM Bayangan elemen yang memberi gaya secara unik, berdasarkan konteksnya.
Mendukung beberapa jenis host dari dalam satu root bayangan
Penggunaan lain untuk :host
adalah jika Anda membuat library tema dan ingin
mendukung gaya banyak jenis elemen host dari dalam Shadow DOM yang sama.
:host(x-foo) {
/* Applies if the host is a <x-foo> element.*/
}
:host(x-foo:host) {
/* Same as above. Applies if the host is a <x-foo> element. */
}
:host(div) {
/* Applies if the host element is a <div>. */
}
Menata gaya internal Shadow DOM dari luar
Pseudo-elemen ::shadow
dan kombinator /deep/
seperti memiliki pedang Vorpal dari otoritas CSS.
Elemen ini memungkinkan penetrasi melalui batas Shadow DOM untuk menata gaya elemen dalam hierarki bayangan.
Pseudo-elemen ::shadow
Jika elemen memiliki setidaknya satu hierarki bayangan, elemen pseudo ::shadow
akan cocok dengan root bayangan itu sendiri.
Dengan ini, Anda dapat menulis pemilih yang menata gaya node internal ke shadow dom elemen.
Misalnya, jika elemen menghosting root bayangan, Anda dapat menulis #host::shadow span {}
untuk menata gaya semua span dalam hierarki bayangannya.
<style>
#host::shadow span {
color: red;
}
</style>
<div id="host">
<span>Light DOM</span>
</div>
<script>
var host = document.querySelector('div');
var root = host.createShadowRoot();
root.innerHTML = `
<span>Shadow DOM</span>
<content></content>
`;
</script>
Contoh (elemen kustom) - <x-tabs>
memiliki turunan <x-panel>
di Shadow DOM-nya. Setiap panel menghosting hierarki bayangan sendiri yang berisi judul h2
. Untuk menata gaya judul tersebut dari halaman utama, Anda dapat menulis:
x-tabs::shadow x-panel::shadow h2 {
...
}
Kombinator /deep/
Kombinator /deep/
mirip dengan ::shadow
, tetapi lebih canggih. Fungsi ini sepenuhnya mengabaikan semua batas bayangan dan melintasi sejumlah hierarki bayangan. Sederhananya, /deep/
memungkinkan Anda melihat bagian dalam elemen dan menargetkan node apa pun.
Kombinator /deep/
sangat berguna di dunia Elemen Khusus, tempat memiliki beberapa tingkat DOM Bayangan adalah hal yang umum. Contoh utamanya adalah menyusun bertingkat sekumpulan elemen kustom (masing-masing menghosting hierarki bayangan sendiri) atau membuat elemen yang diwarisi dari elemen lain menggunakan <shadow>
.
Contoh (elemen kustom) - pilih semua elemen <x-panel>
yang merupakan turunan dari
<x-tabs>
, di mana saja dalam hierarki:
x-tabs /deep/ x-panel {
...
}
Contoh - menata gaya semua elemen dengan class .library-theme
, di mana saja dalam hierarki bayangan:
body /deep/ .library-theme {
...
}
Menggunakan querySelector()
Sama seperti .shadowRoot
yang membuka
hierarki bayangan untuk traversal DOM, pengombinator membuka hierarki bayangan untuk traversal pemilih.
Daripada menulis rantai gila bertingkat, Anda dapat menulis satu pernyataan:
// No fun.
document.querySelector('x-tabs').shadowRoot
.querySelector('x-panel').shadowRoot
.querySelector('#foo');
// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');
Menata gaya elemen native
Kontrol HTML native merupakan tantangan untuk gaya. Banyak orang menyerah
dan membuat sendiri. Namun, dengan ::shadow
dan /deep/
, elemen apa pun di platform web yang menggunakan Shadow DOM dapat diberi gaya. Contoh yang bagus adalah jenis <input>
dan <video>
:
video /deep/ input[type="range"] {
background: hotpink;
}
Membuat hook gaya
Penyesuaian bagus. Dalam kasus tertentu, Anda mungkin ingin membuat lubang di pelindung gaya Shadow dan membuat hook untuk gaya orang lain.
Menggunakan ::shadow dan /deep/
Ada banyak kemampuan di balik /deep/
. Hal ini memberi penulis komponen cara untuk menetapkan
setiap elemen sebagai gaya atau sekumpulan elemen sebagai tema.
Contoh - menata gaya semua elemen yang memiliki class .library-theme
, dengan mengabaikan semua hierarki bayangan:
body /deep/ .library-theme {
...
}
Menggunakan elemen pseudo kustom
WebKit dan
Firefox menentukan elemen pseudo untuk menata gaya bagian internal elemen browser native. Contoh yang baik
adalah input[type=range]
. Anda dapat menata gaya thumb penggeser <span style="color:blue">blue</span>
dengan menargetkan ::-webkit-slider-thumb
:
input[type=range].custom::-webkit-slider-thumb {
-webkit-appearance: none;
background-color: blue;
width: 10px;
height: 40px;
}
Serupa dengan cara browser menyediakan hook gaya ke beberapa internal, penulis konten Shadow DOM dapat menetapkan elemen tertentu sebagai gaya yang dapat diubah oleh pihak luar. Hal ini dilakukan melalui elemen pseudo kustom.
Anda dapat menetapkan elemen sebagai elemen pseudo kustom menggunakan atribut pseudo
.
Nilai, atau namanya, harus diawali dengan "x-". Dengan demikian, akan terbentuk
asosiasi dengan elemen tersebut di hierarki bayangan dan memberi orang luar
jalur yang ditetapkan untuk melintasi batas bayangan.
Berikut adalah contoh pembuatan widget penggeser kustom dan mengizinkan seseorang untuk menata gaya thumb penggesernya menjadi biru:
<style>
#host::x-slider-thumb {
background-color: blue;
}
</style>
<div id="host"></div>
<script>
var root = document.querySelector('#host').createShadowRoot();
root.innerHTML = `
<div>
<div pseudo="x-slider-thumb"></div>' +
</div>
`;
</script>
Menggunakan Variabel CSS
Cara yang efektif untuk membuat hook tema adalah melalui Variabel CSS. Pada dasarnya, membuat "placeholder gaya" untuk diisi oleh pengguna lain.
Bayangkan penulis elemen kustom yang menandai placeholder variabel di Shadow DOM-nya. Satu untuk menata gaya font tombol internal dan satu lagi untuk warnanya:
button {
color: var(--button-text-color, pink); /* default color will be pink */
font-family: var(--button-font);
}
Kemudian, penyempan elemen menentukan nilai tersebut sesuai keinginannya. Mungkin untuk mencocokkan tema Comic Sans yang super keren di halaman mereka sendiri:
#host {
--button-text-color: green;
--button-font: "Comic Sans MS", "Comic Sans", cursive;
}
Karena cara Variabel CSS mewarisi, semuanya berjalan lancar dan ini berfungsi dengan baik. Seluruh gambar akan terlihat seperti ini:
<style>
#host {
--button-text-color: green;
--button-font: "Comic Sans MS", "Comic Sans", cursive;
}
</style>
<div id="host">Host node</div>
<script>
var root = document.querySelector('#host').createShadowRoot();
root.innerHTML = `
<style>
button {
color: var(--button-text-color, pink);
font-family: var(--button-font);
}
</style>
<content></content>
`;
</script>
Mereset gaya
Gaya yang dapat diwariskan seperti font, warna, dan tinggi baris terus memengaruhi elemen
di DOM Bayangan. Namun, untuk fleksibilitas maksimum, Shadow DOM memberi kita
properti resetStyleInheritance
untuk mengontrol apa yang terjadi di batas bayangan.
Anggaplah ini sebagai cara untuk memulai dari awal saat membuat komponen baru.
resetStyleInheritance
false
- Default. Properti CSS yang dapat diwarisi akan terus diwarisi.true
- mereset properti yang dapat diwarisi keinitial
di batas.
Berikut adalah demo yang menunjukkan bagaimana hierarki bayangan terpengaruh dengan mengubah resetStyleInheritance
:
<div>
<h3>Light DOM</h3>
</div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.resetStyleInheritance = <span id="code-resetStyleInheritance">false</span>;
root.innerHTML = `
<style>
h3 {
color: red;
}
</style>
<h3>Shadow DOM</h3>
<content select="h3"></content>
`;
</script>
<div class="demoarea" style="width:225px;">
<div id="style-ex-inheritance"><h3 class="border">Light DOM</div>
</div>
<div id="inherit-buttons">
<button id="demo-resetStyleInheritance">resetStyleInheritance=false</button>
</div>
<script>
var container = document.querySelector('#style-ex-inheritance');
var root = container.createShadowRoot();
//root.resetStyleInheritance = false;
root.innerHTML = '<style>h3{ color: red; }</style><h3>Shadow DOM<content select="h3"></content>';
document.querySelector('#demo-resetStyleInheritance').addEventListener('click', function(e) {
root.resetStyleInheritance = !root.resetStyleInheritance;
e.target.textContent = 'resetStyleInheritance=' + root.resetStyleInheritance;
document.querySelector('#code-resetStyleInheritance').textContent = root.resetStyleInheritance;
});
</script>
Memahami .resetStyleInheritance
sedikit lebih rumit, terutama karena
hanya memengaruhi properti CSS yang dapat diwariskan. Pesan tersebut menyatakan: saat
Anda mencari properti untuk diwarisi, di batas antara halaman dan
ShadowRoot, jangan wariskan nilai dari host, tetapi gunakan nilai
initial
(sesuai spesifikasi CSS).
Jika Anda tidak yakin properti mana yang diwarisi di CSS, lihat daftar praktis ini atau alihkan kotak centang "Tampilkan yang diwarisi" di panel Elemen.
Menata gaya node terdistribusi
Node terdistribusi adalah elemen yang dirender di titik penyisipan (elemen <content>
). Elemen <content>
memungkinkan Anda memilih node dari Light DOM dan merendernya di lokasi yang telah ditentukan di Shadow DOM. Secara logis, elemen tersebut tidak berada di Shadow DOM; elemen tersebut masih merupakan turunan dari elemen host. Titik penyisipan hanyalah hal rendering.
Node terdistribusi mempertahankan gaya dari dokumen utama. Artinya, aturan gaya dari halaman utama akan terus diterapkan ke elemen, meskipun saat dirender di titik penyisipan. Sekali lagi, node terdistribusi secara logis masih berada di dom light dan tidak bergerak. Objek tersebut hanya dirender di tempat lain. Namun, saat node didistribusikan ke Shadow DOM, node tersebut dapat menggunakan gaya tambahan yang ditentukan di dalam hierarki bayangan.
Elemen pseudo ::content
Node yang didistribusikan adalah turunan dari elemen host, jadi bagaimana kita dapat menargetkan
node tersebut dari dalam Shadow DOM? Jawabannya adalah elemen pseudo ::content
CSS.
Ini adalah cara untuk menargetkan node Light DOM yang melewati titik penyisipan. Contoh:
::content > h3
menata gaya tag h3
yang melewati titik penyisipan.
Mari kita lihat contohnya:
<div>
<h3>Light DOM</h3>
<section>
<div>I'm not underlined</div>
<p>I'm underlined in Shadow DOM!</p>
</section>
</div>
<script>
var div = document.querySelector('div');
var root = div.createShadowRoot();
root.innerHTML = `
<style>
h3 { color: red; }
content[select="h3"]::content > h3 {
color: green;
}
::content section p {
text-decoration: underline;
}
</style>
<h3>Shadow DOM</h3>
<content select="h3"></content>
<content select="section"></content>
`;
</script>
Mereset gaya di titik penyisipan
Saat membuat ShadowRoot, Anda memiliki opsi untuk mereset gaya yang diwarisi.
Titik penyisipan <content>
dan <shadow>
juga memiliki opsi ini. Saat menggunakan
elemen ini, tetapkan .resetStyleInheritance
di JS atau gunakan atribut
reset-style-inheritance
boolean pada elemen itu sendiri.
Untuk titik penyisipan ShadowRoot atau
<shadow>
:reset-style-inheritance
berarti properti CSS yang dapat diwarisi ditetapkan keinitial
di host, sebelum mencapai konten bayangan Anda. Lokasi ini dikenal sebagai batas atas.Untuk titik penyisipan
<content>
:reset-style-inheritance
berarti properti CSS yang dapat diwariskan ditetapkan keinitial
sebelum turunan host didistribusikan di titik penyisipan. Lokasi ini dikenal sebagai batas bawah.
Kesimpulan
Sebagai penulis elemen kustom, kita memiliki banyak opsi untuk mengontrol tampilan dan nuansa konten. Shadow DOM membentuk dasar untuk dunia baru yang berani ini.
DOM Bayangan memberi kita enkapsulasi gaya tercakup dan cara untuk memasukkan sebanyak (atau sesedikit) dunia luar yang kita pilih. Dengan menentukan elemen pseudo kustom atau menyertakan placeholder Variabel CSS, penulis dapat memberikan hook gaya yang praktis bagi pihak ketiga untuk menyesuaikan konten mereka lebih lanjut. Secara keseluruhan, penulis web memiliki kontrol penuh terhadap cara konten mereka ditampilkan.