Episode ini membedah template dan shadow DOM: elemen template untuk menyimpan markup yang belum dirender, slot untuk distribusi konten, dan enkapsulasi gaya serta struktur lewat elemen kustom.

Halaman yang rumit menuntut potongan markup yang dipakai berulang-ulang. Episode 18 membahas template HTML dan shadow DOM dasar: elemen template untuk menyimpan markup yang belum dirender, slot untuk distribusi konten, dan shadow DOM yang mengisolasi gaya serta struktur dari dokumen utama.
Konsep ini adalah fondasi Web Components — cara membuat elemen HTML sendiri yang berperilaku dan bergaya mandiri. Memahaminya membuat kalian bisa menyusun antarmuka yang benar-benar dapat dipakai ulang tanpa bergantung pada framework.
template menyimpan fragmen HTML yang tidak ditampilkan saat halaman dimuat. Isinya hanya hidup saat diklon dan disisipkan lewat JavaScript:
<template id="kartu-siswa">
<div class="kartu">
<h3></h3>
<p></p>
</div>
</template>Konten dalam template tidak ikut dirender, tidak memuat gambar, dan tidak bisa dilihat pengguna sampai dipakai. Ini cara yang bersih untuk menyimpan markup bersama dengan dokumennya.
Untuk memakai template, akses properti content, klon dengan cloneNode(true), lalu isi dan sisipkan:
const template = document.getElementById("kartu-siswa");
const fragmen = template.content.cloneNode(true);
fragmen.querySelector("h3").textContent = "Ayu Lestari";
fragmen.querySelector("p").textContent = "Kelas 3A";
document.body.appendChild(fragmen);Argumen true pada cloneNode memastikan seluruh pohon DOM ikut disalin, bukan hanya elemen terluarnya. Setiap pemakaian menghasilkan salinan baru sehingga markup asli tetap utuh.
Shadow DOM adalah pohon DOM terpisah yang menempel pada sebuah elemen host. Buat dengan memanggil attachShadow:
const host = document.getElementById("widget-halo");
const shadow = host.attachShadow({ mode: "open" });
shadow.innerHTML = "<p>Halo dari dalam shadow DOM</p>";Mode open membuat shadow root bisa diakses dari JavaScript luar — coba host.shadowRoot di console untuk memeriksa isinya. Mode closed menyembunyikannya dari akses eksternal, tapi tidak menambah keamanan nyata; mode open lebih umum dan mudah diperiksa.
Aturan CSS halaman induk tidak menembus shadow root secara default. Sebaliknya, gaya di dalam shadow tidak bocor keluar — itulah inti enkapsulasi:
shadow.innerHTML = `
<style>
p { color: #2563eb; font-weight: 700; }
</style>
<p>Halo dari dalam shadow DOM</p>
`;Elemen p di dalam shadow tidak terpengaruh aturan p { color: red } milik halaman, dan sebaliknya. Ini mencegah tabrakan gaya di proyek besar.
Shadow DOM tidak harus tertutup total. Elemen slot membuka lubang yang menerima konten dari luar, diarahkan oleh atribut slot:
<my-kartu>
<span slot="judul">Pengumuman</span>
<span slot="tanggal">10 Agustus 2026</span>
</my-kartu>Di dalam shadow, slot name="judul" memetakan span ber-slot judul ke tempatnya. Konten asli tetap berada di light DOM dan bergaya oleh halaman induk.
Gabungkan semuanya: elemen kustom yang menempelkan shadow root dan merender slotnya saat dibuat:
class KartuKabar extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: "open" });
shadow.innerHTML = `
<style>
.kartu { border: 1px solid #d1d5db; padding: 1rem; }
.judul { font-weight: 700; }
</style>
<div class="kartu">
<div class="judul"><slot name="judul"></slot></div>
<div><slot name="tanggal"></slot></div>
</div>
`;
}
}
customElements.define("my-kartu", KartuKabar);Nama elemen kustom wajib mengandung tanda hubung, misalnya my-kartu, agar tidak berbenturan dengan elemen bawaan. Setelah customElements.define, tag <my-kartu> langsung bisa dipakai di mana saja di halaman.
Buat beberapa kartu dari array data memakai template dan shadow DOM:
const data = [
{ nama: "Ayu", kelas: "3A" },
{ nama: "Bima", kelas: "3B" },
{ nama: "Citra", kelas: "3C" },
];
for (const item of data) {
const fragmen = template.content.cloneNode(true);
fragmen.querySelector("h3").textContent = item.nama;
fragmen.querySelector("p").textContent = "Kelas " + item.kelas;
document.body.appendChild(fragmen);
}Ganti isi data dengan daftar sebenarnya. Karena template diklon setiap iterasi, tidak ada kartu yang berbagi elemen yang sama — aman untuk data dinamis.
Menambahkan template sendiri ke dokumen tidak menampilkan apa pun — ia tetap inert. Selalu klon kontennya dengan cloneNode(true) sebelum disisipkan.
document.querySelector tidak menjangkau shadow root. Untuk mencarinya, akses lewat host.shadowRoot.querySelector(...) ketika mode-nya open.
Episode 18 memperkenalkan cara menyusun komponen yang terisolasi: template untuk markup tersimpan, slot untuk konten dari luar, dan shadow DOM untuk enkapsulasi gaya serta struktur lewat elemen kustom.
Inti yang harus dibawa pulang:
template menyimpan markup yang tidak dirender sampai diklon.cloneNode(true) membuat salinan lengkap pohon DOM.attachShadow membuat pohon DOM terpisah pada elemen host.slot menerima konten dari light DOM dengan atribut slot.Di episode 19 selanjutnya kita akan membahas praktik penulisan HTML yang bersih dan mudah dipelihara — indentasi, hierarki heading, penamaan kelas, dan linting otomatis agar kode markup kalian nyaman dibaca tim.