CSS dan gaya
Artikel ini membahas lebih lanjut hal-hal luar biasa yang dapat Anda lakukan dengan Shadow DOM. Bagian ini dibuat berdasarkan konsep yang dibahas dalam Shadow DOM 101. Jika Anda mencari pengantar, lihat artikel tersebut.
Pengantar
Mari kita hadapi. Tidak ada yang seksi tentang 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. Class ini memiliki banyak properti yang 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!
Menata 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 kekhususan yang lebih tinggi daripada aturan :host
yang ditentukan dalam elemen, tetapi memiliki kekhususan 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>
Penerapan 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 membuat 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 Shadow DOM elemen yang menata gayanya secara unik, berdasarkan konteksnya.
Mendukung beberapa jenis host dari dalam satu shadow root
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 DOM Bayangan dari luar
Pseudo-elemen ::shadow
dan pengombinator /deep/
seperti memiliki pedang Vorpal dari otoritas CSS.
Elemen ini memungkinkan menembus batas Shadow DOM untuk menata gaya elemen dalam hierarki bayangan.
Pseudo-elemen ::shadow
Jika sebuah elemen memiliki setidaknya satu pohon bayangan, elemen semu ::shadow
akan cocok dengan root bayangan itu sendiri.
Hal ini memungkinkan Anda 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 pohon bayangannya 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. Fitur ini sepenuhnya mengabaikan semua batas bayangan dan menyeberang ke sejumlah pohon 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 Shadow DOM 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
pohon bayangan untuk traversal DOM, pengombinator membuka pohon 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 yang 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 perisai penataan gaya Shadow dan membuat hook untuk ditata oleh 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 bagus
adalah input[type=range]
. Anda dapat menata gaya ibu jari 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 dapat diberi gaya 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 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 efektif untuk membuat hook tema adalah melalui Variabel CSS. Pada dasarnya, membuat "placeholder gaya" untuk diisi oleh pengguna lain.
Bayangkan seorang penulis elemen kustom yang menandai placeholder variabel dalam 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 Shadow DOM. 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 terus diwariskan.true
- mereset properti yang dapat diwariskan 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. Tertulis: saat
Anda mencari properti untuk diwarisi, pada batas antara halaman dan
ShadowRoot, jangan warisi nilai dari host, tetapi gunakan nilai
initial
(sesuai spesifikasi CSS).
Jika Anda tidak yakin properti mana yang diwarisi dalam CSS, lihat daftar praktis ini atau pilih kotak centang "Tampilkan warisan" 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, {i>node<i} terdistribusi masih secara logis berada di {i>light dom<i} dan tidak bergerak. Mereka hanya dirender di tempat lain. Namun, saat didistribusikan ke Shadow DOM, node dapat menggunakan gaya tambahan yang ditentukan di dalam hierarki shadow.
Elemen pseudo ::content
Node terdistribusi 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 pada 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 diwarisi 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.
Shadow DOM memberi kita enkapsulasi gaya tercakup dan cara untuk memasukkan sebanyak (atau sesedikit) dunia luar yang kita pilih. Dengan menentukan elemen semu kustom atau menyertakan placeholder Variabel CSS, penulis dapat memberikan hook gaya visual yang mudah digunakan kepada pihak ketiga untuk menyesuaikan kontennya lebih lanjut. Secara keseluruhan, penulis web memiliki kontrol penuh terhadap cara konten mereka ditampilkan.