CSS dan penataan gaya
Artikel ini membahas lebih banyak hal luar biasa yang dapat Anda lakukan dengan Shadow DOM. Artikel ini dibuat berdasarkan konsep yang dibahas dalam Shadow DOM 101. Jika Anda mencari pengantar, lihat artikel tersebut.
Pengantar
Mari kita hadapi. Markup tanpa gaya tidak menarik. Untungnya, orang-orang hebat di balik Komponen Web sudah memperkirakan hal ini dan tidak membiarkan kita kebingungan. Modul Cakupan CSS menentukan banyak opsi untuk menata gaya konten di hierarki shadow.
Enkapsulasi gaya
Salah satu fitur inti Shadow DOM adalah batas shadow. Shadow DOM 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 oleh karena itu diberi gaya merah, adalah yang ada di ShadowRoot. Sekali lagi, gaya tercakup secara default.
- Aturan gaya lain yang ditentukan di halaman ini yang menargetkan h3 tidak akan memengaruhi konten saya. Hal ini karena pemilih tidak melewati batas shadow.
Moral dari kisah ini? 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 shadow tree:
<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 spesifisitas yang lebih tinggi daripada aturan :host
yang ditentukan dalam elemen, tetapi spesifisitasnya lebih rendah daripada atribut style
yang ditentukan pada elemen host. Hal ini memungkinkan pengguna mengganti gaya 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 - cocok hanya jika elemen itu sendiri memiliki class .different (misalnya, <x-foo class="different"></x-foo>):
:host(.different) {
...
}
Bereaksi terhadap status pengguna
Kasus penggunaan umum untuk :host adalah saat Anda membuat Elemen Kustom dan ingin bereaksi terhadap status pengguna yang berbeda (: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 memberi tema pada elemen berdasarkan sekelilingnya. Misalnya,
banyak orang membuat tema dengan menerapkan class ke <html> atau <body>:
<body class="different">
<x-foo></x-foo>
</body>
Anda dapat menggunakan :host-context(.different) untuk menata gaya <x-foo> jika merupakan turunan dari elemen dengan class .different:
:host-context(.different) {
color: red;
}
Hal ini memberi Anda kemampuan untuk mengenkapsulasi aturan gaya dalam Shadow DOM elemen yang memberinya gaya 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 penataan gaya berbagai 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
Elemen semu ::shadow dan kombinator /deep/ seperti memiliki pedang Vorpal otoritas CSS.
Pemilih ini memungkinkan penembusan batas Shadow DOM untuk menata gaya elemen dalam hierarki shadow.
Elemen semu ::shadow
Jika elemen memiliki setidaknya satu hierarki shadow, elemen semu ::shadow akan cocok dengan root shadow itu sendiri.
Dengan begitu, Anda dapat menulis pemilih yang menata gaya node internal ke shadow DOM elemen.
Misalnya, jika elemen menghosting root shadow, Anda dapat menulis #host::shadow span {} untuk menata gaya semua span dalam hierarki shadow-nya.
<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 <x-panel> turunan di Shadow DOM-nya. Setiap panel menghosting hierarki 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. Pemilih ini sepenuhnya mengabaikan semua batas shadow dan melintasi sejumlah shadow tree. Sederhananya, /deep/ memungkinkan Anda melihat lebih dalam isi elemen dan menargetkan node mana pun.
Kombinator /deep/ sangat berguna di dunia Elemen Kustom yang biasanya memiliki beberapa tingkat Shadow DOM. Contoh utamanya adalah menyusun bertingkat banyak elemen kustom (masing-masing menghosting shadow tree-nya sendiri) atau membuat elemen yang mewarisi dari elemen lain menggunakan <shadow>.
Contoh (elemen kustom) - pilih semua elemen <x-panel> yang merupakan turunan dari
<x-tabs>, di mana pun dalam hierarki:
x-tabs /deep/ x-panel {
...
}
Contoh - menata gaya semua elemen dengan class .library-theme, di mana saja dalam hierarki shadow:
body /deep/ .library-theme {
...
}
Menggunakan querySelector()
Sama seperti .shadowRoot yang membuka
shadow tree untuk penjelajahan DOM, kombinator membuka shadow tree untuk penjelajahan pemilih.
Daripada menulis rantai bertingkat yang rumit, 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 sulit untuk diberi gaya. Banyak orang menyerah begitu saja
dan membuat solusi 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 itu bagus. Dalam kasus tertentu, Anda mungkin ingin membuat lubang pada pelindung gaya Shadow dan membuat hook untuk diberi gaya oleh orang lain.
Menggunakan ::shadow dan /deep/
Ada banyak kemampuan di balik /deep/. Hal ini memberi penulis komponen cara untuk menetapkan
elemen individual sebagai dapat diberi gaya atau sejumlah elemen sebagai dapat diberi tema.
Contoh - menata gaya semua elemen yang memiliki class .library-theme, dengan mengabaikan semua shadow tree:
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 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;
}
Mirip dengan cara browser menyediakan hook gaya ke beberapa internal, pembuat 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-". Tindakan ini akan membuat
pengaitan dengan elemen tersebut di shadow tree dan memberi pihak luar
jalur yang ditentukan untuk melintasi batas shadow.
Berikut adalah contoh pembuatan widget penggeser kustom dan mengizinkan seseorang untuk menata gaya tombol 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 seorang pembuat 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, penyemat elemen menentukan nilai tersebut sesuai keinginan mereka. Mungkin agar sesuai dengan tema Comic Sans yang sangat keren di halaman mereka sendiri:
#host {
--button-text-color: green;
--button-font: "Comic Sans MS", "Comic Sans", cursive;
}
Karena cara Variabel CSS diwariskan, semuanya berjalan lancar dan berfungsi dengan baik. Gambaran keseluruhannya 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 shadow.
Anggap saja ini sebagai cara untuk memulai dari awal saat membuat komponen baru.
resetStyleInheritance
false- Default. Properti CSS yang dapat diwariskan terus diwariskan.true- mereset properti yang dapat diwariskan keinitialpada batas.
Berikut adalah demo yang menunjukkan pengaruh perubahan resetStyleInheritance terhadap shadow tree:
<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 berpengaruh pada properti CSS yang dapat diwariskan. Artinya: saat
Anda mencari properti untuk diwarisi, di batas antara halaman dan
ShadowRoot, jangan mewarisi 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 centang kotak "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. Elemen tersebut tidak secara logis berada di Shadow DOM; elemen tersebut masih merupakan turunan dari elemen host. Titik penyisipan hanyalah hal rendering.
Node yang didistribusikan mempertahankan gaya dari dokumen utama. Artinya, aturan gaya dari halaman utama terus berlaku untuk elemen, bahkan saat dirender di titik penyisipan. Sekali lagi, node yang didistribusikan masih secara logis berada di light DOM dan tidak berpindah. Iklan tersebut hanya dirender di tempat lain. Namun, saat node didistribusikan ke Shadow DOM, node tersebut dapat menggunakan gaya tambahan yang ditentukan di dalam shadow tree.
Elemen pseudo ::content
Node terdistribusi adalah turunan dari elemen host, jadi bagaimana kita dapat menargetkannya 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 apa pun 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 boolean
reset-style-inheritance pada elemen itu sendiri.
Untuk titik penyisipan ShadowRoot atau
<shadow>:reset-style-inheritanceberarti properti CSS yang dapat diwariskan ditetapkan keinitialdi host, sebelum mencapai konten shadow Anda. Lokasi ini dikenal sebagai batas atas.Untuk titik penyisipan
<content>:reset-style-inheritanceberarti properti CSS yang dapat diwariskan ditetapkan keinitialsebelum 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 kita. Shadow DOM menjadi dasar bagi dunia baru yang berani ini.
Shadow DOM memberi kita enkapsulasi gaya tercakup dan sarana untuk membiarkan dunia luar masuk sebanyak (atau sesedikit) yang kita pilih. Dengan menentukan elemen pseudo kustom atau menyertakan placeholder Variabel CSS, penulis dapat memberikan hook gaya yang praktis kepada pihak ketiga untuk menyesuaikan konten mereka lebih lanjut. Secara keseluruhan, penulis web memiliki kontrol penuh atas cara konten mereka ditampilkan.