Manfaat komponen web adalah kegunaannya kembali: Anda dapat membuat widget UI sekali, dan menggunakannya kembali beberapa kali. Sementara Anda memerlukan JavaScript untuk membuat komponen web, Anda tidak memerlukan pustaka JavaScript. HTML dan API terkait menyediakan semua yang Anda butuhkan.
Standar Komponen Web terdiri atas tiga bagian—template HTML, Elemen Kustom, dan Shadow DOM. Jika digabungkan, keduanya memungkinkan pembuatan elemen mandiri (dienkapsulasi) yang disesuaikan, dapat digunakan kembali yang dapat terintegrasi dengan lancar ke dalam aplikasi yang ada, seperti semua elemen HTML lainnya yang telah kita bahas.
Di bagian ini, kita akan membuat elemen <star-rating>
, yakni komponen web yang memungkinkan pengguna memberi rating pengalaman pada
skala satu
hingga lima bintang. Saat memberi nama elemen kustom, sebaiknya gunakan semua huruf kecil. Juga, sertakan tanda hubung,
karena ini membantu membedakan antara elemen biasa dan khusus.
Kita akan membahas penggunaan elemen <template>
dan <slot>
, atribut slot
, dan JavaScript untuk membuat template dengan
Shadow DOM yang dienkapsulasi. Kemudian kita akan menggunakan kembali elemen yang sudah ditentukan, menyesuaikan bagian teks,
seperti halnya elemen atau komponen web. Kita juga akan membahas secara singkat penggunaan CSS dari dalam dan luar elemen kustom.
Elemen <template>
Elemen <template>
digunakan untuk mendeklarasikan fragmen HTML yang akan di-clone dan disisipkan ke dalam DOM dengan JavaScript. Konten elemen tidak dirender secara default. Sebaliknya, dibuat instance-nya menggunakan JavaScript.
<template id="star-rating-template">
<form>
<fieldset>
<legend>Rate your experience:</legend>
<rating>
<input type="radio" name="rating" value="1" aria-label="1 star" required />
<input type="radio" name="rating" value="2" aria-label="2 stars" />
<input type="radio" name="rating" value="3" aria-label="3 stars" />
<input type="radio" name="rating" value="4" aria-label="4 stars" />
<input type="radio" name="rating" value="5" aria-label="5 stars" />
</rating>
</fieldset>
<button type="reset">Reset</button>
<button type="submit">Submit</button>
</form>
</template>
Karena konten elemen <template>
tidak ditulis ke layar, <form>
dan kontennya tidak dirender.
Ya, Codepen ini kosong, tetapi jika Anda memeriksa tab HTML, Anda akan melihat markup <template>
.
Dalam contoh ini, <form>
bukan turunan dari <template>
di DOM. Sebaliknya, konten elemen <template>
adalah turunan
dari DocumentFragment
yang ditampilkan oleh HTMLTemplateElement.content
saat ini. Agar terlihat, JavaScript harus digunakan untuk mengambil konten dan menambahkan konten tersebut ke DOM.
JavaScript singkat ini tidak membuat elemen kustom. Sebaliknya, contoh ini telah menambahkan konten <template>
ke dalam <body>
.
Konten telah menjadi bagian dari DOM yang dapat dilihat dan ditata.
Mewajibkan JavaScript menerapkan template untuk peringkat bintang satu saja tidak terlalu berguna, namun membuat komponen web untuk berulang kali digunakan, widget rating bintang yang dapat disesuaikan sangatlah berguna.
Elemen <slot>
Kami menyertakan slot untuk menyertakan legenda yang disesuaikan per kejadian. HTML menyediakan <slot>
sebagai placeholder di dalam <template>
yang, jika diberi nama, akan membuat "slot bernama". Slot bernama dapat digunakan
untuk menyesuaikan konten
dalam komponen web. Elemen <slot>
memberi cara untuk mengontrol tempat turunan dari
harus dimasukkan ke dalam pohon bayangannya.
Dalam template, kita mengubah <legend>
menjadi <slot>
:
<template id="star-rating-template">
<form>
<fieldset>
<slot name="star-rating-legend">
<legend>Rate your experience:</legend>
</slot>
Atribut name
digunakan untuk menetapkan slot ke elemen lain jika elemen memiliki atribut slot yang nilainya cocok dengan
nama slot yang dinamai. Jika elemen kustom tidak memiliki kecocokan untuk slot, konten <slot>
akan dirender.
Jadi, kami menyertakan <legend>
dengan konten generik yang boleh dirender jika seseorang menyertakan <star-rating></star-rating>
, tanpa konten, di HTML-nya.
<star-rating>
<legend slot="star-rating-legend">Blendan Smooth</legend>
</star-rating>
<star-rating>
<legend slot="star-rating-legend">Hoover Sukhdeep</legend>
</star-rating>
<star-rating>
<legend slot="star-rating-legend">Toasty McToastface</legend>
<p>Is this text visible?</p>
</star-rating>
Atribut slot adalah atribut global yang digunakan
untuk mengganti konten <slot>
dalam <template>
. Dalam elemen khusus, elemen dengan atribut slot
adalah <legend>
. Tidak harus seperti itu. Dalam template kita, <slot name="star-rating-legend">
akan diganti dengan <anyElement slot="star-rating-legend">
,
di mana <anyElement>
dapat berupa elemen apa pun, bahkan elemen kustom lainnya.
Elemen yang belum ditentukan
Di <template>
, kita menggunakan elemen <rating>
. Elemen ini bukan elemen kustom. Sebaliknya, itu adalah elemen yang tidak diketahui. Browser
tidak gagal ketika mereka
tidak mengenali suatu elemen. Elemen HTML yang tidak dikenal diperlakukan oleh browser sebagai sebaris anonim
elemen yang dapat diberi gaya dengan CSS. Serupa dengan <span>
, elemen <rating>
dan <star-rating>
tidak menerapkan agen pengguna
gaya atau semantik.
Perhatikan bahwa <template>
dan kontennya tidak dirender. <template>
adalah elemen yang diketahui
berisi konten yang
tidak akan dirender. Elemen <star-rating>
belum ditentukan. Browser akan menampilkannya hingga kita menentukannya
seperti semua elemen yang tidak dikenal. Untuk saat ini, <star-rating>
yang tidak dikenal diperlakukan sebagai elemen inline anonim, sehingga konten
termasuk legenda dan <p>
di <star-rating>
ketiga akan ditampilkan seperti halnya jika berada di <span>
.
Mari kita tentukan elemen untuk mengonversi elemen yang tidak dikenal ini menjadi elemen kustom.
Elemen khusus
JavaScript diperlukan untuk menentukan elemen kustom. Jika ditentukan, konten elemen <star-rating>
akan diganti dengan
{i>shadow root<i} yang berisi semua konten {i>template<i} yang kita kaitkan dengannya. Elemen <slot>
dari template diganti
dengan konten elemen dalam <star-rating>
yang nilai atribut slot
-nya cocok dengan nilai nama <slot>
, jika
hanya ada satu. Jika tidak, konten slot template akan ditampilkan.
Konten dalam elemen kustom yang tidak terkait dengan slot—<p>Is this text visible?</p>
dalam <star-rating>
ketiga kami—tidak disertakan dalam
akar bayangan dan karenanya tidak ditampilkan.
Kami menentukan elemen khusus yang bernama star-rating
dengan memperluas HTMLElement
:
customElements.define('star-rating',
class extends HTMLElement {
constructor() {
super(); // Always call super first in constructor
const starRating = document.getElementById('star-rating-template').content;
const shadowRoot = this.attachShadow({
mode: 'open'
});
shadowRoot.appendChild(starRating.cloneNode(true));
}
});
Setelah elemen ditentukan, setiap kali browser menemukan elemen <star-rating>
, elemen tersebut akan dirender seperti yang ditentukan
oleh elemen dengan #star-rating-template
, yang merupakan template kita. Browser akan melampirkan hierarki shadow DOM ke node, dengan menambahkan
clone konten template ke shadow DOM tersebut.
Perhatikan bahwa elemen tempat Anda dapat attachShadow()
dibatasi.
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(starRating.cloneNode(true));
Jika melihat alat developer, Anda akan mengetahui bahwa <form>
dari <template>
adalah bagian dari shadow root setiap elemen kustom.
Duplikat konten <template>
terlihat di setiap elemen kustom di alat developer dan terlihat di browser, tetapi kontennya
elemen kustom itu sendiri tidak dirender ke layar.
Dalam contoh <template>
, kita menambahkan konten template ke isi dokumen, dengan menambahkan konten ke DOM biasa.
Dalam definisi customElements
, kita menggunakan metode
appendChild()
, tetapi konten template yang digandakan ditambahkan ke
shadow DOM yang dienkapsulasi.
Perhatikan bagaimana bintang kembali menjadi tombol pilihan tanpa gaya? Menjadi bagian dari shadow DOM daripada DOM standar, gaya dalam tab CSS Codepen tidak berlaku. CSS tab tersebut gaya tercakup pada dokumen, bukan pada shadow DOM, sehingga gaya tidak diterapkan. Kita harus membuat paket data untuk menata gaya konten Shadow DOM yang dienkapsulasi.
DOM Bayangan
Shadow DOM mencakup gaya CSS ke setiap shadow tree, mengisolasinya dari bagian lain dokumen. Ini berarti CSS eksternal tidak berlaku untuk komponen Anda, dan gaya komponen tidak berpengaruh pada bagian dokumen lainnya, kecuali jika kita sengaja mengarahkan mereka.
Karena telah menambahkan konten ke shadow DOM, kita dapat menyertakan elemen <style>
menyediakan CSS yang dienkapsulasi ke elemen khusus.
Dengan cakupan elemen kustom, kita tidak perlu khawatir gaya akan menyebar ke seluruh dokumen. Kita dapat
secara substansial mengurangi kekhususan pemilih. Misalnya, karena satu-satunya input yang digunakan dalam elemen kustom adalah pilihan radio
kita dapat menggunakan input
alih-alih input[type="radio"]
sebagai pemilih.
<template id="star-rating-template">
<style>
rating {
display: inline-flex;
}
input {
appearance: none;
margin: 0;
box-shadow: none;
}
input::after {
content: '\2605'; /* solid star */
font-size: 32px;
}
rating:hover input:invalid::after,
rating:focus-within input:invalid::after {
color: #888;
}
input:invalid::after,
rating:hover input:hover ~ input:invalid::after,
input:focus ~ input:invalid::after {
color: #ddd;
}
input:valid {
color: orange;
}
input:checked ~ input:not(:checked)::after {
color: #ccc;
content: '\2606'; /* hollow star */
}
</style>
<form>
<fieldset>
<slot name="star-rating-legend">
<legend>Rate your experience:</legend>
</slot>
<rating>
<input type="radio" name="rating" value="1" aria-label="1 star" required/>
<input type="radio" name="rating" value="2" aria-label="2 stars"/>
<input type="radio" name="rating" value="3" aria-label="3 stars"/>
<input type="radio" name="rating" value="4" aria-label="4 stars"/>
<input type="radio" name="rating" value="5" aria-label="5 stars"/>
</rating>
</fieldset>
<button type="reset">Reset</button>
<button type="submit">Submit</button>
</form>
</template>
Sementara komponen web dienkapsulasi dengan markup dalam-<template>
dan gaya CSS dicakup ke shadow DOM dan tersembunyi
dari segala sesuatu di luar komponen, konten slot yang dirender, <anyElement slot="star-rating-legend">
dari <star-rating>
, tidak dienkapsulasi.
Penataan gaya di luar cakupan saat ini
Dimungkinkan saja, namun tidak sederhana, untuk menata gaya dokumen dari dalam shadow DOM dan menata gaya materi shadow DOM dari gaya global. Batas bayangan, tempat shadow DOM berakhir dan DOM reguler dimulai, bisa dilintasi, namun hanya dengan sengaja.
Pohon bayangan adalah hierarki DOM di dalam shadow DOM. Root bayangan adalah node root dari pohon bayangan.
Class semu :host
memilih <star-rating>
, elemen host bayangan.
Shadow host adalah node DOM tempat shadow DOM terpasang. Untuk menargetkan versi host tertentu saja, gunakan :host()
.
Tindakan ini hanya akan memilih elemen bayangan host yang cocok dengan parameter yang diteruskan, seperti pemilih atribut atau class. Untuk memilih
semua elemen kustom, Anda dapat menggunakan star-rating { /* styles */ }
di CSS global, atau :host(:not(#nonExistantId))
di gaya template. Dalam hal
dengan kekhususan, CSS global yang unggul.
Elemen pseudo ::slotted()
melewati batas shadow DOM
dari dalam shadow DOM. Elemen ini memilih elemen yang diberi slot jika cocok dengan pemilih. Dalam contoh kita, ::slotted(legend)
cocok dengan tiga legenda.
Untuk menargetkan shadow DOM dari CSS dalam cakupan global, template harus diedit. part
dapat ditambahkan ke elemen apa pun yang ingin Anda tata gayanya. Lalu, gunakan elemen pseudo ::part()
untuk mencocokkan elemen dalam pohon bayangan
yang cocok dengan parameter yang diteruskan. Anchor atau elemen asal elemen-elemen semu merupakan
host, atau nama elemen kustom, dalam hal ini star-rating
. Parameternya adalah nilai atribut part
.
Jika markup template kami dimulai seperti berikut:
<template id="star-rating-template">
<form part="formPart">
<fieldset part="fieldsetPart">
Kita dapat menargetkan <form>
dan <fieldset>
dengan:
star-rating::part(formPart) { /* styles */ }
star-rating::part(fieldsetPart) { /* styles */ }
Nama bagian berfungsi mirip dengan class: elemen dapat memiliki beberapa nama bagian yang dipisahkan spasi, dan beberapa elemen dapat memiliki nama suku cadang yang sama.
Google memiliki checklist yang bagus untuk membuat elemen kustom. Anda mungkin juga ingin mempelajari tentang shadow DOM deklaratif.
Menguji pemahaman Anda
Uji pengetahuan Anda tentang {i>template<i}, slot, dan bayangan.
Secara default gaya dari luar shadow DOM akan menata gaya elemen di dalamnya.
Jawaban mana yang merupakan deskripsi yang benar dari elemen <template>
?