Belajar Membuat Custom Element Dan Shadow DOM Dasar di Javascript

DAFTAR ISI

Ihsan Magazine – Saat ini kita akan mencoba belajar membuat Custom Element dasar di Javascript, apa itu Custom Element? Mudah nya Custom Element seperti kita Membuat Tag HTML baru untuk Front End Developer.

Bisa untuk memperluas elemen bawaan HTML, dan masih banyak yang lain, dasarnya memakai Elements global dipakai untuk mengartikan elemen khusus dan mengajarkan tag baru pada browser.

Panggil customElements.define() dengan nama tag yang mau Kamu buat dan class JavaScript yang memperluas HTMLElement dasar.

Selengkapnya Baca Disini

Saat ini Study Kasus dengan ClubFinder.zip

Bila kita telaah dengan seksama, pada proyek Club Finder terdapat 4 (empat) bagian yang kemungkinan untuk dijadikan custom element, yaitu:

  1. App Bar : Komponen di posisi atas yang menunjukkan identitas atau nama dari aplikasi web.
  2. Search Bar : Komponen yang terdiri atas elemen <input> dan <button> dan berguna untuk melakukan pencarian club sesuai dengan input pemakai.
  3. Club Daftar : Komponen yang berguna untuk menampung data dari hasil pencarian, kemudian menampilkannya dalam wujud daftar.
  4. Club Item : Komponen yang memperlihatkan data individual club yang diberikan dari club daftar. Komponen ini terdiri atas gambar, nama, dan penjelasan singkat club.

Solution: Membuat app-bar Component

Apakah Kamu berhasil menerapkan custom element pada proyek Club Finder? Bila belum, ayo kita lakukan bersama-sama. Kita mulai dari komponen paling mudah terlebih dulu yaitu App Bar.
Supaya mengurus berkas pada proyek jadi lebih gampang, kita harus membuat folder baru dengan nama “component” di dalam folder src -> script.
20200310192559e057df7d65da0d6b4369a31dfa0bcf60.png
Folder ini akan menampung berkas JavaScript yang dipakai dalam membuat custom element.
Kemudian di dalam folder component, buat berkas JavaScript baru dengan nama “app-bar.js”. Lalu kita buat class dengan nama AppBar yang mewarisi sifat HTMLElement.
    1. class AppBar extends HTMLElement {
    1.   
    1. }
Lalu di dalam body block classnya, kita implementasi method connectedCallback dan membuat fungsi render.
    1. class AppBar extends HTMLElement {
    1.    connectedCallback(){
    1.  
    1.    }
    1.  
    1.    render() {
    1.       
    1.    }
    1. }
Seperti yang telah kita ketahui, connectedCallback() akan terpanggil saat element sudah diimplementasikan pada DOM. Bila kita mau element ini saat diterapkan langsung melakukan rendering maka kita bisa memanggil fungsi this.render() di dalam connectedCallback.
    1. class AppBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.  
    1.    }
    1. }
Kemudian pada fungsi render, kita tuliskan kode yang berguna untuk menampilkan elemen yang diperlukan pada melalui properti this.innerHTML. Apa saja yang diperlukan? Kita dapat menatapnya pada berkas index.html.
    1. <header>
    1.        <div id=“appBar” class=“app-bar”>
    1.            <h2>Club Finder</h2>
    1.        </div>
    1. </header>
Di dalam elemen <header> terdapat elemen <div> yang menerapkan class “app-bar”. Nah kita copy element di dalam app-bar, dan paste untuk dijadikan nilai pada this.innerHTML di fungsi render().
    1. class AppBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `<h2>Club Finder</h2>`;
    1.    }
    1. }
Kemudian di akhir berkas app-bar.js, jangan sampai lupa untuk definisikan custom element yang kita buat supaya bisa dipakai pada DOM.
    1. class AppBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `<h2>Club Finder</h2>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“app-bar”, AppBar);
Dengan seperti itu kita bisa merubah penerapan app-bar pada index.html dengan memakai tag <app-bar>.
    1. <header>
    1.       <app-bar></app-bar>
    1. </header>
Terakhir, supaya kode pada berkas app-bar.js tereksekusi, impor berkas app-bar.js pada berkas app.js, semacam ini:
    1. import “./src/script/component/app-bar.js”;
Tuliskan kode itu di awal berkas app.js, sehingga keseluruhan kode pada berkasnya akan terlihat semacam ini:
    1. import “./src/script/component/app-bar.js”;
    1. import main from “./src/script/view/main.js”;
    1.  
    1. document.addEventListener(“DOMContentLoaded”, main);
Kemudian coba kita buka proyeknya memakai local server. Inilah tampilan hasilnya:
2020031019350448bf1ff8d96767443908e81b171563c3.png
Oops, tampilan App Bar terlihat berantakan. Kita harus memperbaiki css yang dipakai pada elemen App Bar sebelumnya. Buka berkas appbar.css lalu ubah selector-nya dari .app-bar jadi app-bar.
    1. appbar {
    1.    padding: 16px;
    1.    width: 100%;
    1.    backgroundcolor: cornflowerblue;
    1.    color: white;
    1.    boxshadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1. }
Lalu lihat kita lihat hasilnya.
2020031019360718a18a9301e96c9237e9d2bd80d3dda2.png
Yah, sekarang teks “Club Finder” tidak terlihat sebab background element tidak bekerja dengan bagus. Mengapa begini yah? Pasalnya, custom element standarnya merupakan inline element, sehingga tak akan mengisi panjang lebar parent element-nya. Solusinya yaitu dengan merubah sifat inline pada custom element jadi block dengan metode menambahkan properti display dengan nilai block pada selector app-bar.
    1. appbar {
    1.    display: block;
    1.    padding: 16px;
    1.    width: 100%;
    1.    backgroundcolor: cornflowerblue;
    1.    color: white;
    1.    boxshadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1. }
Dengan seperti itu tampilan kita berhasil membuat custom element <app-bar> dengan bagus!
20200310193753886f026632febaa362d6775a39bbc28d.png

Membuat search-bar Component

Pembuatan elemen <search-bar> lebih sedikit rumit dari pembuatan komponen sebelumnya, sebab di dalam komponen search bar terdapat element <input> dan <button>. Perpaduan kedua element itu dipakai dalam mencari data club. Sedapat mungkin kita membuat custom element <search-bar> sehingga   memudahkan kala memakai komponen itu.
Ayo kita mulai dengan membikin berkas JavaScript baru dengan nama search-bar.js. Lalu di dalamnya kita membuat class SearchBar dengan mewarisi sifat HTMLElement.
    1. class SearchBar extends HTMLElement {
    1.   
    1. }
Lalu kita implementasi method connectedCallback dan membuat fungsi render.
    1. class SearchBar extends HTMLElement {
    1.    connectedCallback(){
    1.  
    1.    }
    1.   
    1.    render() {
    1.       
    1.    }
    1. }
Lalu panggil fungsi render() di dalam connectedCallback().
    1. class SearchBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.   
    1.    render() {
    1.  
    1.    }
    1. }
Di dalam fungsi render kita ambil elemen yang diperlukan untuk ditampilkan dari berkas index.html.
    1. <div id=“search-container” class=“search-container”>
    1.    <input placeholder=“Search football club” id=“searchElement” type=“search”>
    1.     <button id=“searchButtonElement” type=“submit”>Search</button>
    1. </div>
Supaya mudah, copy semua kode itu dan paste untuk dijadikan nilai this.innerHTML di dalam fungsi render.
    1. class SearchBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.   
    1.    render() {
    1.        this.innerHTML = `
    1.        <div id=”search-container” class=”search-container”>
    1.            <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1.            <button id=”searchButtonElement” type=”submit”>Search</button>
    1.        </div>
    1.        `;
    1.    }
    1. }
Sebab di dalam elemen ini terdapat <button> yang mesti mempunyai sebuah event saat dia ditekan, maka kita perlu menyediakan setter. Gunanya untuk menetapkan fungsi event supaya bisa mudah diterapkan dari luar class SearchBar.
    1. class SearchBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.   
    1.    set clickEvent(event) {
    1.        this._clickEvent = event;
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.        <div id=”search-container” class=”search-container”>
    1.            <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1.            <button id=”searchButtonElement” type=”submit”>Search</button>
    1.        </div>
    1.        `;
    1.    }
    1. }
Kemudian kita terapkan this._clickEvent sebagai event pada element <button> dengan metode menuliskan kode berikut di akhir fungsi render():
    1. this.querySelector(“#searchButtonElement”).addEventListener(“click”, this._clickEvent);
Sehingga kode pada fungsi render akan terlihat semacam ini:
    1. render() {
    1.   this.innerHTML = `
    1.     <div id=”search-container” class=”search-container”>
    1.        <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1.        <button id=”searchButtonElement” type=”submit”>Search</button>
    1.     </div>`;
    1.  
    1.    this.querySelector(“#searchButtonElement”).addEventListener(“click”, this._clickEvent);
    1. }
Dengan seperti itu nantinya kita bisa mudah dalam clickEvent pada SearchBar yang dipakai di berkas main.js.
Pada berkas main.js juga kita memanfaatkan value dari element <input> agar mendapatkan kata kunci pencarian club. Supaya mudah mendapatkan nilai value dari elemen <input> yang terdapat di search bar, kita buat fungsi getter yang mengembalikan nilai value dari elemen <input> itu.
    1. get value() {
    1.    return this.querySelector(“#searchElement”).value;
    1. }
Sehingga keseluruhan kode yang ada berkas search-bar.js akan tampak semacam ini:
    1. class SearchBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.   
    1.    set clickEvent(event) {
    1.        this._clickEvent = event;
    1.        this.render();
    1.    }
    1.  
    1.    get value() {
    1.        return this.querySelector(“#searchElement”).value;
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.        <div id=”search-container” class=”search-container”>
    1.            <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1.            <button id=”searchButtonElement” type=”submit”>Search</button>
    1.        </div>
    1.        `;
    1.  
    1.        this.querySelector(“#searchButtonElement”).addEventListener(“click”, this._clickEvent);
    1.    }
    1. }
Kemudian di akhir berkasnya, jangan sampai lupa untuk definisikan custom element yang kita buat supaya bisa dipakai pada DOM.
    1. class SearchBar extends HTMLElement {
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.   
    1.    set clickEvent(event) {
    1.        this._clickEvent = event;
    1.        this.render();
    1.    }
    1.  
    1.    get value() {
    1.        return this.querySelector(“#searchElement”).value;
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.        <div id=”search-container” class=”search-container”>
    1.            <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1.            <button id=”searchButtonElement” type=”submit”>Search</button>
    1.        </div>
    1.        `;
    1.  
    1.        this.querySelector(“#searchButtonElement”).addEventListener(“click”, this._clickEvent);
    1.    }
    1. }
    1.  
    1. customElements.define(“search-bar”, SearchBar);
Yeay, pembuatan custom element telah selesai. Sekarang waktunya kita memakainya! Pertama ubahlah struktur html yang membentuk komponen pencarian dengan memakai tag <search-bar>. Silakan buka berkas index.html kemudian ubah kode berikut:
    1. <div id=“search-container” class=“search-container”>
    1.     <input placeholder=“Search football club” id=“searchElement” type=“search”>
    1.     <button id=“searchButtonElement” type=“submit”>Search</button>
    1. </div>
Jadi semacam ini:
    1. <search-bar></search-bar>
Sesudah itu, buka berkas src -> script -> view -> main.js dan sesuaikan kode binding elemen berikut:
    1. const searchElement = document.querySelector(“#searchElement”);
Dengan merubah selector-nya jadi “search-bar”.
    1. const searchElement = document.querySelector(“search-bar”);
Kemudian kita tidak memerlukan deklarasi variabel buttonSearchElement sebab sekarang kita bisa mengakses button pada komponen pencarian melalui searchElement. Jadi silakan hapus deklarasi variabel berikut:
    1. const buttonSearchElement = document.querySelector(“#searchButtonElement”);
Lalu kita sesuaikan kembali penerapan event click pada komponen pencarian dengan merubah kode berikut:
    1. buttonSearchElement.addEventListener(“click”, onButtonSearchClicked);
Jadi:
    1. searchElement.clickEvent = onButtonSearchClicked;
Terakhir, sebab berkas main.js perlu kode pada berkas search-bar.js tereksekusi, kita lakukan impor berkas search-bar.js pada berkas main.js, semacam ini:
    1. import ‘../component/search-bar.js’;
Tuliskan kode itu di awal berkas main.js, sehingga keseluruhan kode pada berkasnya akan terlihat semacam ini:
    1. import ‘../component/search-bar.js’;
    1. import DataSource from ‘../data/data-source.js’;
    1.  
    1. const main = () => {
    1.    const searchElement = document.querySelector(“search-bar”);
    1.    const clubListElement = document.querySelector(“#clubList”);
    1.  
    1.    const onButtonSearchClicked = async () => {
    1.        try {
    1.            const result = await DataSource.searchClub(searchElement.value);
    1.            renderResult(result);
    1.        } catch (message) {
    1.            fallbackResult(message)
    1.        }
    1.    };
    1.  
    1.    const renderResult = results => {
    1.        clubListElement.innerHTML = “”;
    1.        results.forEach(club => {
    1.            const { name, fanArt, description } = club;
    1.            const clubElement = document.createElement(“div”);
    1.            clubElement.setAttribute(“class”, “club”);
    1.  
    1.            clubElement.innerHTML = `
    1.                <img class=”fan-art-club” src=”${fanArt}” alt=”Fan Art”>
    1.                <div class=”club-info”>
    1.                    <h2>${name}</h2>
    1.                    <p>${description}</p>
    1.                </div>`;
    1.  
    1.            clubListElement.appendChild(clubElement);
    1.        })
    1.    };
    1.  
    1.    const fallbackResult = message => {
    1.        clubListElement.innerHTML = “”;
    1.        clubListElement.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
    1.    };
    1.  
    1.    searchElement.clickEvent = onButtonSearchClicked;
    1. };
    1.  
    1. export default main;
Kemudian coba kita buka proyeknya memakai local server lalu lakukan pencarian dengan memakai kata kunci “Arsenal”.  Hasilnya ialah tampilan berikut:
202003102009124b6eec15399d963670bbeffeecf30a42.png

Solution: Membuat club-list dan club-item Component

Custom element berikutnya yang harus kita buat ialah <club-list> dan <club-item>. Masih ingat terkait Nested Custom Element? Nah dalam membuat kedua custom element ini kita akan memakai custom element di dalam custom element. Atau sering disebut dengan nested custom element.
Ayo kita awali dengan membikin dua berkas JavaScript baru dengan nama “club-list.js” dan “club-item.js” pada src -> script -> component.

Membuat <club-list> element

Tahap pertama kita buat custom element <club-list> terlebih dulu. Pada berkas club-list.js, kita buat class ClubList dengan mewarisi sifat HTMLElement.
    1. class ClubList extends HTMLElement {
    1. }
Lalu kita buat 2 (dua) fungsi di dalamnya yaitu setter clubs, dan render.
    1. class ClubList extends HTMLElement {
    1.    set clubs(clubs) {
    1.  
    1.    }
    1.  
    1.    render() {
    1.       
    1.    }
    1. }
Fungsi set clubs dipakai untuk menetapkan properti this._clubs pada class ini. Nantinya properti itu akan dipakai pada fungsi render dalam membuat custom element <club-item>.
    1. set clubs(clubs) {
    1.      this._clubs = clubs;
    1.      this.render();
    1. }
Lalu di dalam fungsi render, kita lakukan proses perulangan dengan memakai forEach pada this._clubs. Pada setiap iterasinya kita akan mendapatkan individual club dan di waktu itu juga kita buat custom element <club-item>. Pada tiap elemen <club-item> dibuat sebagai child dari element <club-list> ini. Hasilnya. fungsi render akan terlihat semacam ini:
    1. render() {
    1.        this.innerHTML = “”;
    1.        this._clubs.forEach(club => {
    1.            const clubItemElement = document.createElement(“club-item”);
    1.            clubItemElement.club = club
    1.            this.appendChild(clubItemElement);
    1.        })
    1. }
Perlu satu fungsi lagi pada custom element ini, yaitu fungsi untuk menangani saat hasil pencarian mengalami kegagalan atau tidak ditemukkan. Oleh sebab itu ayo kita buat fungsi dengan nama renderError() dengan satu buah patokan yang merupakan pesan eror/alasan yang mesti ditampilkan.
    1. renderError(message) {
    1.  
    1. }
Untuk template html yang akan ditampilkan, kita bisa copy dari fungsi fallbackResult pada berkas src -> script -> view -> main.js.
    1. clubListElement.innerHTML = “”;
    1. clubListElement.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
Lalu paste pada fungsi renderError() dan ubah clubListElement.innerHTML jadi this.innerHTML.
    1. renderError(message) {
    1.        this.innerHTML = “”;
    1.        this.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
    1. }
Di akhir berkas club-list.js jangan sampai lupa untuk definisikan custom element yang kita buat supaya bisa dipakai pada DOM.
    1. customElements.define(“club-list”, ClubList);
Oh ya! Sebab pada berkas ini kita memakai elemen <club-item> yang nanti akan dituliskan pada berkas club-item.js, maka kita harus melakukan impor berkas club-item.js di berkas ini.
    1. import ‘./club-item.js’;
Sehingga sekarang keseluruhan kode yang terdapat di berkas ini akan terlihat semacam ini:
    1. import ‘./club-item.js’;
    1.  
    1. class ClubList extends HTMLElement {
    1.    set clubs(clubs) {
    1.        this._clubs = clubs;
    1.        this.render();
    1.    }
    1.  
    1.    renderError(message) {
    1.        this.innerHTML = “”;
    1.        this.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = “”;
    1.        this._clubs.forEach(club => {
    1.            const clubItemElement = document.createElement(“club-item”);
    1.            clubItemElement.club = club
    1.            this.appendChild(clubItemElement);
    1.        })
    1.    }
    1. }
    1.  
    1. customElements.define(“club-list”, ClubList);
Pembuatan element <club-list> selesai! Sekarang kita lanjut dengan membikin elemen <club-item>.

Membuat <club-item> element

Pada berkas club-item.js, kita buat class ClubItem dengan mewarisi sifat HTMLElement.
    1. class ClubItem extends HTMLElement {
    1.   
    1. }
Lalu kita buat fungsi setter club dan fungsi render.
    1. class ClubItem extends HTMLElement {
    1.    set club(club) {
    1.  
    1.    }
    1.  
    1.    render() {
    1.       
    1.    }
    1. }
Fungsi setter club berguna untuk menetapkan nilai club ke properti this._club yang kemudian akan dipakai pada fungsi render untuk menampilkan data individual club hasil pencarian. Sehingga kita sesuaikan kode di dalam fungsi setter club jadi semacam ini:
    1. class ClubItem extends HTMLElement {
    1.    set club(club) {
    1.        this._club = club;
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.  
    1.    }
    1. }
Kemudian kita copy template html yang berada di fungsi renderResult di berkas src -> script -> view -> main.js.
    1. clubElement.innerHTML = `
    1.          <img class=”fan-art-club” src=”${fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.               <h2>${name}</h2>
    1.               <p>${description}</p>
    1. </div>`;
Kemudian paste template html pada this.innerHTML melalui fungsi render().
    1. class ClubItem extends HTMLElement {
    1.    set club(club) {
    1.        this._club = club;
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.            <img class=”fan-art-club” src=”${fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.                <h2>${name}</h2>
    1.                <p>${description}</p>
    1.            </div>`;
    1.    }
    1. }
Kemudian kita sesuaikan kembali properti-properti yang dipakai pada html template, jadi semacam ini:
    1. class ClubItem extends HTMLElement {
    1.    set club(club) {
    1.        this._club = club;
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.            <img class=”fan-art-club” src=”${this._club.fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.                <h2>${this._club.name}</h2>
    1.                <p>${this._club.description}</p>
    1.            </div>`;
    1.    }
    1. }
Sebab pada this._club inilah properti dari objek club disimpan.
Lalu pada akhir berkas club-item.js jangan sampai lupa untuk definisikan custom element yang kita buat supaya bisa dipakai pada DOM.
    1. class ClubItem extends HTMLElement {
    1.    set club(club) {
    1.        this._club = club;
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.            <img class=”fan-art-club” src=”${this._club.fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.                <h2>${this._club.name}</h2>
    1.                <p>${this._club.description}</p>
    1.            </div>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“club-item”, ClubItem);
Dengan seperti itu elemen <club-item> telah siap dipakai.

Memakai <club-list> element

Sesudah membuat kedua custom element yang diperlukan, sekarang waktunya kita memakainya!
Silakan buka berkas index.html, kemudian ubah penerapan club daftar memakai elemen <div> berikut:
    1. <div id=“clubList”></div>
Jadi:
    1. <club-list></club-list>
Berikutnya buka berkas src -> script -> view -> main.js. Kita sesuaikan kembali selector di waktu melakukan binding clubListElement. Ubah kode berikut:
    1. const clubListElement = document.querySelector(“#clubList”);
Jadi:
    1. const clubListElement = document.querySelector(“club-list”);
Kemudian kita sesuaikan juga kode yang ada di dalam fungsi renderResult. Hapus seluruh logika yang terdapat di dalam fungsi itu.
    1. const renderResult = results => {
    1.        clubListElement.innerHTML = “”;
    1.        results.forEach(club => {
    1.            const { name, fanArt, description } = club;
    1.            const clubElement = document.createElement(“div”);
    1.            clubElement.setAttribute(“class”, “club”);
    1.  
    1.            clubElement.innerHTML = `
    1.                <img class=”fan-art-club” src=”${fanArt}” alt=”Fan Art”>
    1.                <div class=”club-info”>
    1.                    <h2>${name}</h2>
    1.                    <p>${description}</p>
    1.                </div>`;
    1.  
    1.            clubListElement.appendChild(clubElement);
    1.        })
    1. };
Kita cukup menggantinya dengan semacam ini:
    1. const renderResult = results => {
    1.      clubListElement.clubs = results;
    1. };
Sesuaikan juga kode yang terdapat di fungsi fallbackResult, sebab kita telah membuat fungsi renderError() pada ClubList, maka penggunaanya cukup dilakukan semacam ini:
    1. const fallbackResult = message => {
    1.        clubListElement.renderError(message);
    1. };
Sebab kita memakai elemen <club-list> pada berkas main.js, maka kita harus melakukan impor berkas club-list.js pada berkas main.js.
    1. import ‘../component/club-list.js’;
Dengan seperti itu keseluruhan kode pada berkas main.js akan terlihat seperti berikut:
    1. import ‘../component/club-list.js’;
    1. import ‘../component/search-bar.js’;
    1. import DataSource from ‘../data/data-source.js’;
    1.  
    1. const main = () => {
    1.    const searchElement = document.querySelector(“search-bar”);
    1.    const clubListElement = document.querySelector(“club-list”);
    1.  
    1.    const onButtonSearchClicked = async () => {
    1.        try {
    1.            const result = await DataSource.searchClub(searchElement.value);
    1.            renderResult(result);
    1.        } catch (message) {
    1.            fallbackResult(message)
    1.        }
    1.    };
    1.  
    1.    const renderResult = results => {
    1.        clubListElement.clubs = results;
    1.    };
    1.  
    1.    const fallbackResult = message => {
    1.        clubListElement.renderError(message);
    1.    };
    1.  
    1.    searchElement.clickEvent = onButtonSearchClicked;
    1. };
    1.  
    1. export default main;
Sekarang kita coba buka proyeknya memakai local server lalu klik tombol pencarian. Voila, inilah tampilan hasilnya:
202003102031456392a6f8c00cbb02e2dc53ed846c7d2a.png
Ops, tampilan daftar club terlihat berantakan. Kita harus menyesuaikan styling-nya juga. Jadi silakan buka berkas src -> style -> clublist.css. Kemudian ubah seluruh selector #clubList jadi club-list dan selector .club jadi club-item.
    1. clubdaftar {
    1.    margintop: 32px;
    1.    width: 100%;
    1.    padding: 16px;
    1. }
    1.  
    1. clubdaftar > .placeholder {
    1.    fontweight: lighter;
    1.    color: rgba(0,0,0,0.5);
    1.    webkituserselect: none;
    1.    mozuserselect: none;
    1.    msuserselect: none;
    1.    userselect: none;
    1. }
    1.  
    1. clubitem {
    1.    marginbottom: 18px;
    1.    boxshadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.    borderradius: 10px;
    1.    overflow: hidden;
    1. }
    1.  
    1. clubitem .fanartclub {
    1.    width: 100%;
    1.    maxheight: 300px;
    1.    objectfit: cover;
    1.    objectposition: center;
    1. }
    1.  
    1. .clubinfo {
    1.    padding: 24px;
    1. }
    1.  
    1. .clubinfo > h2 {
    1.    fontweight: lighter;
    1. }
    1.  
    1. .clubinfo > p {
    1.    margintop: 10px;
    1.    overflow: hidden;
    1.    textoverflow: ellipsis;
    1.    display: webkitbox;
    1.    webkitboxorient: vertical;
    1.    webkitlineclamp: 10; /* number of lines to show */
    1. }
Kemudian tambahkan juga properti display dengan nilai block pada selector club-list dan club-item.
    1. clubdaftar {
    1.    display: block;
    1.    ….
    1. }
    1.  
    1. ….
    1.  
    1. clubitem {
    1.    display: block;
    1.    ….
    1. }
    1.  
    1. ….
Sehingga keseluruhan kode pada berkas clublist.css akan terlihat semacam ini:
    1. clubdaftar {
    1.    display: block;
    1.    margintop: 32px;
    1.    width: 100%;
    1.    padding: 16px;
    1. }
    1.  
    1. clubdaftar > .placeholder {
    1.    fontweight: lighter;
    1.    color: rgba(0,0,0,0.5);
    1.    webkituserselect: none;
    1.    mozuserselect: none;
    1.    msuserselect: none;
    1.    userselect: none;
    1. }
    1.  
    1. clubitem {
    1.    display: block;
    1.    marginbottom: 18px;
    1.    boxshadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.    borderradius: 10px;
    1.    overflow: hidden;
    1. }
    1.  
    1. clubitem .fanartclub {
    1.    width: 100%;
    1.    maxheight: 300px;
    1.    objectfit: cover;
    1.    objectposition: center;
    1. }
    1.  
    1. .clubinfo {
    1.    padding: 24px;
    1. }
    1.  
    1. .clubinfo > h2 {
    1.    fontweight: lighter;
    1. }
    1.  
    1. .clubinfo > p {
    1.    margintop: 10px;
    1.    overflow: hidden;
    1.    textoverflow: ellipsis;
    1.    display: webkitbox;
    1.    webkitboxorient: vertical;
    1.    webkitlineclamp: 10; /* number of lines to show */
    1. }
Sekarang kita coba buka kembali proyek club finder dengan memakai local server. Semestinya sekarang semuanya telah berjalan dengan bagus.
20200310203504226e0e544df5e8e4b9b389c9fa9f3f0c.png
Langkah dari ketiga solution ini dapat Kamu temukan pula pada repository berikut:
https://github.com/dicodingacademy/a163-bfwd-labs/tree/109-club-finder-custom-element-solution

Shadow DOM

Baca Juga:  Diboikot Pemerintahan Trump, Tiongkok: Kasus ZTE Penuh Omong Kosong

Menerapkan Shadow DOM pada Proyek Club Finder

Kita mulai dari <app-bar> component ayo. Pertama kita buka dulu proyek club finder dengan text editor yang kita pakai.
20200313095904249af3ba1d687f1216c8dc44d5fa11fc.png
Lalu buka berkas script -> component -> app-bar.js, buat constructor dari class itu dan di dalamnya kita tetapkan shadow root semacam ini:
    1. class AppBar extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `<h2>Club Finder</h2>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“app-bar”, AppBar);
Sebab kita telah menerapkan Shadow DOM pada AppBar, jangan sampai lupa pada fungsi render(), kita ubah this.innerHTML jadi this.shadowDOM.innerHTML.
    1. class AppBar extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = `<h2>Club Finder</h2>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“app-bar”, AppBar);
Lalu buka berkas style -> appbar.css dan pindahkan (cut) semua kode yang ada di berkas itu.
    1. appbar {
    1.    display: block;
    1.    padding: 16px;
    1.    width: 100%;
    1.    backgroundcolor: cornflowerblue;
    1.    color: white;
    1.    boxshadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1. }
Lalu tempel (paste) pada nilai this.shadowDOM.innerHTML dengan dibungkus oleh element <style> tepat sebelum element <h2> pada fungsi render() di berkas app-bar.js semacam ini:
    1. class AppBar extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = `
    1.        <style>
    1.            app-bar {
    1.                display: block;
    1.                padding: 16px;
    1.                width: 100%;
    1.                background-color: cornflowerblue;
    1.                color: white;
    1.                box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.            }
    1.        </style>
    1.        <h2>Club Finder</h2>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“app-bar”, AppBar);
Coba kita simpan perubahan yang diterapkan kemudian lihat perubahannya pada browser.
202003131025191a8dcdd75e697b30cba119d06defa3e1.png
Ups, pada browser kita bisa melihat title yang ditampilkan pada <app-bar> terlihat berantakan. Untuk menanganinya, kita harus menyesuaikan kembali style yang diimplementasikan pada custom element jadi semacam ini:
    1. class AppBar extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = `
    1.        <style>
    1.            * {
    1.                margin: 0;
    1.                padding: 0;
    1.                box-sizing: border-box;
    1.            }
    1.            :host {
    1.                display: block;
    1.                width: 100%;
    1.                background-color: cornflowerblue;
    1.                color: white;
    1.                box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.            }
    1.            h2 {
    1.                padding: 16px;
    1.            }
    1.        </style>
    1.        <h2>Club Finder</h2>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“app-bar”, AppBar);
Pada perubahan styling itu kita menambahkan
    1. * {
    1.     margin: 0;
    1.     padding: 0;
    1.     boxsizing: borderbox;
    1. }
Yang dipakai untuk menghilangkan semua margin dan padding standar yang diimplementasikan pada element html. Dan kita juga merubah pengaturan box-sizing jadi border-box.
Lalu kode pada kode styling lainnya juga kita memandang bahwa selector app-bar digantikan dengan :host. Apa itu :host? Selector :host merupakan selector yang dipakai untuk menunjuk element :host yang menerapkan Shadow DOM. Pada host kita tidak bisa mengatur padding sehingga kita harus memindahkannya pada elemen <h2>.
Sesudah melakukan perubahan itu simpan (save) kembali perubahannya dan lihat hasilnya pada browser, semestinya <app-bar> telah ditampilkan dengan bagus.
20200313102750873f1782c7d560202cb13b08a7eb95ea.png
Sebab kita sudah memerlukan lagi berkas src -> styles -> appbar.css, kita bisa menghapus berkas itu.
20200313102817f716d852a60606d8028fd6f20b702380.png
Jangan sampai lupa untuk menghapus import css itu pada src -> styles -> style.css.
    1. @import “clublist.css”;
    1. @import “searchbar.css”;
    1.  
    1. * {
    1.    padding: 0;
    1.    margin: 0;
    1.    boxsizing: borderbox;
    1. }
    1.  
    1. body {
    1.    fontfamily: sansserif;
    1. }
    1.  
    1. main {
    1.    width: 90%;
    1.    maxwidth: 800px;
    1.    margin: 32px auto;
    1. }
Baca Juga:  Lebih Murah, iPhone 7 Rekondisi Dijual Rp6 Jutaan

Menerapkan Shadow DOM pada Search Bar

Sesudah berhasil menerapkan Shadow DOM pada App Bar, berikutnya kita terapkan Shadow DOM pada search bar. Silakan buka berkas src -> script -> component -> search-bar.js, kemudian buat constructor dan terapkan Shadow DOM di dalamnya.
    1. class SearchBar extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.   
    1.    set clickEvent(event) {
    1.        this._clickEvent = event;
    1.        this.render();
    1.    }
    1.  
    1.    get value() {
    1.        return this.querySelector(“#searchElement”).value;
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.        <div id=”search-container” class=”search-container”>
    1.            <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1.            <button id=”searchButtonElement” type=”submit”>Search</button>
    1.        </div>
    1.        `;
    1.  
    1.        this.querySelector(“#searchButtonElement”).addEventListener(“click”, this._clickEvent);
    1.    }
    1. }
    1.  
    1. customElements.define(“search-bar”, SearchBar);
Sama seperti yang kita lakukan pada component App Bar, kita ubah this.innerHTML jadi this.shadowDOM.InnerHTML pada fungsi render().
    1. class SearchBar extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    connectedCallback(){
    1.        this.render();
    1.    }
    1.   
    1.    set clickEvent(event) {
    1.        this._clickEvent = event;
    1.        this.render();
    1.    }
    1.  
    1.    get value() {
    1.        return this.querySelector(“#searchElement”).value;
    1.    }
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = `
    1.        <div id=”search-container” class=”search-container”>
    1.            <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1.            <button id=”searchButtonElement” type=”submit”>Search</button>
    1.        </div>
    1.        `;
    1.  
    1.        this.querySelector(“#searchButtonElement”).addEventListener(“click”, this._clickEvent);
    1.    }
    1. }
    1.  
    1. customElements.define(“search-bar”, SearchBar);
Disamping itu juga kita ubah pemanggilan this.querySelector jadi this.shadowDOM.querySelector pada fungsi render() dan get value().
    1. class SearchBar extends HTMLElement {
    1.  
    1.  ……….
    1.  
    1.    get value() {
    1.        return this.shadowDOM.querySelector(“#searchElement”).value;
    1.    }
    1.  
    1.    render() {
    1.       ………
    1.        this.shadowDOM.querySelector(“#searchButtonElement”).addEventListener(“click”, this._clickEvent);
    1.    }
    1. }
    1.  
    1.  
    1. ………..
Lalu buka berkas src -> styles -> searchbar.css, pindahkan (cut) semua kode yang terdapat di berkas itu.
    1. .searchcontainer {
    1.    maxwidth: 800px;
    1.    boxshadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.    padding: 16px;
    1.    borderradius: 5px;
    1.    display: flex;
    1.    position: sticky;
    1.    top: 10px;
    1.    backgroundcolor: white;
    1. }
    1.  
    1. .searchcontainer > input {
    1.    width: 75%;
    1.    padding: 16px;
    1.    border: 0;
    1.    borderbottom: 1px solid cornflowerblue;
    1.    fontweight: bold;
    1. }
    1.  
    1. .searchcontainer > input:focus {
    1.    outline: 0;
    1.    borderbottom: 2px solid cornflowerblue;
    1. }
    1.  
    1. .searchcontainer > input:focus::placeholder {
    1.    fontweight: bold;
    1. }
    1.  
    1. .searchcontainer >  input::placeholder {
    1.    color: cornflowerblue;
    1.    fontweight: normal;
    1. }
    1.  
    1. .searchcontainer > button {
    1.    width: 23%;
    1.    cursor: pointer;
    1.    marginleft: auto;
    1.    padding: 16px;
    1.    backgroundcolor: cornflowerblue;
    1.    color: white;
    1.    border: 0;
    1.    texttransform: uppercase;
    1. }
    1.  
    1. @media screen and (maxwidth: 550px){
    1.    .searchcontainer {
    1.        flexdirection: column;
    1.        position: static;
    1.    }
    1.  
    1.    .searchcontainer > input {
    1.        width: 100%;
    1.        marginbottom: 12px;
    1.    }
    1.  
    1.    .searchcontainer > button {
    1.        width: 100%;
    1.    }
    1. }
Lalu tempel (paste) pada nilai this.shadowDOM.innerHTML dengan dibungkus oleh element <style> tepat sebelum element <div> pada fungsi render() di berkas search-bar.js semacam ini:
    1. class SearchBar extends HTMLElement {
    1.  
    1. ………
    1.  
    1. render() {
    1. this.shadowDOM.innerHTML = `
    1. <style>
    1. .search-container {
    1. max-width: 800px;
    1. box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1. padding: 16px;
    1. border-radius: 5px;
    1. display: flex;
    1. position: sticky;
    1. top: 10px;
    1. background-color: white;
    1. }
    1. .search-container > input {
    1. width: 75%;
    1. padding: 16px;
    1. border: 0;
    1. border-bottom: 1px solid cornflowerblue;
    1. font-weight: bold;
    1. }
    1. .search-container > input:focus {
    1. outline: 0;
    1. border-bottom: 2px solid cornflowerblue;
    1. }
    1. .search-container > input:focus::placeholder {
    1. font-weight: bold;
    1. }
    1. .search-container > input::placeholder {
    1. color: cornflowerblue;
    1. font-weight: normal;
    1. }
    1. .search-container > button {
    1. width: 23%;
    1. cursor: pointer;
    1. margin-left: auto;
    1. padding: 16px;
    1. background-color: cornflowerblue;
    1. color: white;
    1. border: 0;
    1. text-transform: uppercase;
    1. }
    1. @media screen and (max-width: 550px){
    1. .search-container {
    1. flex-direction: column;
    1. position: static;
    1. }
    1. .search-container > input {
    1. width: 100%;
    1. margin-bottom: 12px;
    1. }
    1. .search-container > button {
    1. width: 100%;
    1. }
    1. }
    1. </style>
    1. <div id=”search-container” class=”search-container”>
    1. <input placeholder=”Search football club” id=”searchElement” type=”search”>
    1. <button id=”searchButtonElement” type=”submit”>Search</button>
    1. </div>
    1. `;
    1. …….
    1. }
    1. }
    1.  
    1. customElements.define(“search-bar”, SearchBar);
Simpan perubahan yang dilakukan kemudian lihat hasilnya pada browser.
202003131034259aa04d43b00877a7efd968c647ba2ae0.png
Komponen Search Bar terlihat normal dan berfungsi dengan bagus sehingga kita tak harus menyesuaikan lagi styling-nya.
Sebab kita sudah memerlukan lagi berkas src -> styles -> searchbar.css, kita bisa menghapus berkas itu.
20200313103452d010492337b319b09b72b75402e29540.png
Jangan sampai lupa untuk menghapus import css itu pada src -> styles -> style.css.
    1. @import “clublist.css”;
    1.  
    1. * {
    1.    padding: 0;
    1.    margin: 0;
    1.    boxsizing: borderbox;
    1. }
    1.  
    1. body {
    1.    fontfamily: sansserif;
    1. }
    1.  
    1. main {
    1.    width: 90%;
    1.    maxwidth: 800px;
    1.    margin: 32px auto;
    1. }

Menerapkan Shadow DOM pada Club Daftar dan Club Item

Terakhir kita terapkan Shadow DOM pada komponen club daftar dan club item. Silakan buka berkas src -> script -> component -> club-list.js, kemudian buat constructor dan terapkan Shadow DOM di dalamnya.
    1. import ‘./club-item.js’;
    1.  
    1. class ClubList extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    set clubs(clubs) {
    1.        this._clubs = clubs;
    1.        this.render();
    1.    }
    1.  
    1.    renderError(message) {
    1.        this.innerHTML = “”;
    1.        this.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = “”;
    1.        this._clubs.forEach(club => {
    1.            const clubItemElement = document.createElement(“club-item”);
    1.            clubItemElement.club = club
    1.            this.appendChild(clubItemElement);
    1.        })
    1.    }
    1. }
    1.  
    1. customElements.define(“club-list”, ClubList);
Kemudian ubah semua kode this.innerHTML jadi this.shadowDOM.innerHTML dan this.appendChild jadi this.shadowDOM.appendChild.
    1. import ‘./club-item.js’;
    1.  
    1. class ClubList extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    set clubs(clubs) {
    1.        this._clubs = clubs;
    1.        this.render();
    1.    }
    1.  
    1.    renderError(message) {
    1.        this.shadowDOM.innerHTML = “”;
    1.        this.shadowDOM.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
    1.    }
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = “”;
    1.        this._clubs.forEach(club => {
    1.            const clubItemElement = document.createElement(“club-item”);
    1.            clubItemElement.club = club
    1.            this.shadowDOM.appendChild(clubItemElement);
    1.        })
    1.    }
    1. }
    1.  
    1. customElements.define(“club-list”, ClubList);
Lalu buka berkas src -> styles -> clublist.css dan pindahkan (cut) kode styling dengan selector club-list > .placeholder
    1. clubdaftar > .placeholder {
    1.    fontweight: lighter;
    1.    color: rgba(0,0,0,0.5);
    1.    webkituserselect: none;
    1.    mozuserselect: none;
    1.    msuserselect: none;
    1.    userselect: none;
    1. }
Lalu tempel (paste) pada nilai this.shadowDOM.innerHTML dengan dibungkus oleh element <style> tepat sebelum element <h2> fungsi renderError() di berkas club-list.js semacam ini:
    1. import ‘./club-item.js’;
    1.  
    1. class ClubList extends HTMLElement {
    1.  
    1.  ………
    1.  
    1.    renderError(message) {
    1.        this.shadowDOM.innerHTML = `
    1.        <style>
    1.            club-list > .placeholder {
    1.                font-weight: lighter;
    1.                color: rgba(0,0,0,0.5);
    1.                -webkit-user-select: none;
    1.                -moz-user-select: none;
    1.                -ms-user-select: none;
    1.                user-select: none;
    1.            }
    1.        </style>
    1.        `;
    1.        this.shadowDOM.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
    1.    }
    1.  
    1.  …….
    1. }
    1.  
    1. customElements.define(“club-list”, ClubList);
Hapus child selector (>) bersama kombinatornya, sisakan .placeholder sebagai selector dari styling itu. Sehingga kode pada berkas ini semuanya terlihat seperti:
    1. import ‘./club-item.js’;
    1.  
    1. class ClubList extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    set clubs(clubs) {
    1.        this._clubs = clubs;
    1.        this.render();
    1.    }
    1.  
    1.    renderError(message) {
    1.        this.shadowDOM.innerHTML = `
    1.        <style>
    1.            .placeholder {
    1.                font-weight: lighter;
    1.                color: rgba(0,0,0,0.5);
    1.                -webkit-user-select: none;
    1.                -moz-user-select: none;
    1.                -ms-user-select: none;
    1.                user-select: none;
    1.            }
    1.        </style>
    1.        `;
    1.        this.shadowDOM.innerHTML += `<h2 class=”placeholder”>${message}</h2>`;
    1.    }
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = “” ;
    1.        this._clubs.forEach(club => {
    1.            const clubItemElement = document.createElement(“club-item”);
    1.            clubItemElement.club = club
    1.            this.shadowDOM.appendChild(clubItemElement);
    1.        })
    1.    }
    1. }
    1.  
    1. customElements.define(“club-list”, ClubList);
Simpan perubahan itu dan lihat hasilnya pada browser, tampilan dari list club akan sangat berantakan.
202003131040024f3e1630a04c7004c7c638df8dbe006e.png
Tenang kita akan memperbaikinya dengan beranjak ke berkas src -> script -> component -> club-item.js.
Pada berkas itu buat sebuah constructor dan terapkan Shadow DOM di dalamnya.
    1. class ClubItem extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    set club(club) {
    1.        this._club = club;
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.innerHTML = `
    1.            <img class=”fan-art-club” src=”${this._club.fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.                <h2>${this._club.name}</h2>
    1.                <p>${this._club.description}</p>
    1.            </div>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“club-item”, ClubItem);
Seperti biasa jangan sampai lupa untuk merubah this.innerHTML jadi this.shadowDOM.innerHTML ya.
    1. class ClubItem extends HTMLElement {
    1.  
    1.    constructor() {
    1.        super();
    1.        this.shadowDOM = this.attachShadow({mode: “open”});
    1.    }
    1.  
    1.    set club(club) {
    1.        this._club = club;
    1.        this.render();
    1.    }
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = `
    1.            <img class=”fan-art-club” src=”${this._club.fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.                <h2>${this._club.name}</h2>
    1.                <p>${this._club.description}</p>
    1.            </div>`;
    1.    }
    1. }
    1.  
    1. customElements.define(“club-item”, ClubItem);
Berikutnya buka kembali berkas src -> styles -> clublist.css dan pindahkan styling berikut:
    1. clubitem {
    1.    display: block;
    1.    marginbottom: 18px;
    1.    boxshadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.    borderradius: 10px;
    1.    overflow: hidden;
    1. }
    1.  
    1. clubitem .fanartclub {
    1.    width: 100%;
    1.    maxheight: 300px;
    1.    objectfit: cover;
    1.    objectposition: center;
    1. }
    1.  
    1. .clubinfo {
    1.    padding: 24px;
    1. }
    1.  
    1. .clubinfo > h2 {
    1.    fontweight: lighter;
    1. }
    1.  
    1. .clubinfo > p {
    1.    margintop: 10px;
    1.    overflow: hidden;
    1.    textoverflow: ellipsis;
    1.    display: webkitbox;
    1.    webkitboxorient: vertical;
    1.    webkitlineclamp: 10; /* number of lines to show */
    1. }
Tempel pada nilai this.shadowDOM.innerHTML dengan dibungkus oleh element <style> tepat sebelum element <img> pada fungsi render() di berkas club-item.js semacam ini:
    1. class ClubItem extends HTMLElement {
    1.  
    1.  …….
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = `
    1.            <style>
    1.                club-item {
    1.                    display: block;
    1.                    margin-bottom: 18px;
    1.                    box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.                    border-radius: 10px;
    1.                    overflow: hidden;
    1.                }
    1.               
    1.                club-item .fan-art-club {
    1.                    width: 100%;
    1.                    max-height: 300px;
    1.                    object-fit: cover;
    1.                    object-position: center;
    1.                }
    1.               
    1.                .club-info {
    1.                    padding: 24px;
    1.                }
    1.               
    1.                .club-info > h2 {
    1.                    font-weight: lighter;
    1.                }
    1.               
    1.                .club-info > p {
    1.                    margin-top: 10px;
    1.                    overflow: hidden;
    1.                    text-overflow: ellipsis;
    1.                    display: -webkit-box;
    1.                    -webkit-box-orient: vertical;
    1.                    -webkit-line-clamp: 10; /* number of lines to show */
    1.                }
    1.            </style>
    1.            <img class=”fan-art-club” src=”${this._club.fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.                <h2>${this._club.name}</h2>
    1.                <p>${this._club.description}</p>
    1.            </div>`;
    1.    }
    1. }
    1.  
    1. ……
Sesuaikan kembali selector pada styling itu jadi semacam ini:
    1. class ClubItem extends HTMLElement {
    1.  
    1.   …..
    1.  
    1.    render() {
    1.        this.shadowDOM.innerHTML = `
    1.            <style>
    1.                * {
    1.                    margin: 0;
    1.                    padding: 0;
    1.                    box-sizing: border-box;
    1.                }
    1.                :host {
    1.                    display: block;
    1.                    margin-bottom: 18px;
    1.                    box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
    1.                    border-radius: 10px;
    1.                    overflow: hidden;
    1.                }
    1.               
    1.                .fan-art-club {
    1.                    width: 100%;
    1.                    max-height: 300px;
    1.                    object-fit: cover;
    1.                    object-position: center;
    1.                }
    1.               
    1.                .club-info {
    1.                    padding: 24px;
    1.                }
    1.               
    1.                .club-info > h2 {
    1.                    font-weight: lighter;
    1.                }
    1.               
    1.                .club-info > p {
    1.                    margin-top: 10px;
    1.                    overflow: hidden;
    1.                    text-overflow: ellipsis;
    1.                    display: -webkit-box;
    1.                    -webkit-box-orient: vertical;
    1.                    -webkit-line-clamp: 10; /* number of lines to show */
    1.                }
    1.            </style>
    1.            <img class=”fan-art-club” src=”${this._club.fanArt}” alt=”Fan Art”>
    1.            <div class=”club-info”>
    1.                <h2>${this._club.name}</h2>
    1.                <p>${this._club.description}</p>
    1.            </div>`;
    1.    }
    1. }
    1.  
    1. ……..
Simpan perubahan itu dan lihat pada browser, semestinya tampilan daftar tim telah kembali normal.
202003131046287ce90b85747ab537976360b93f4a2da2.png
Oh ya, sebelum beranjak kita buka kembali berkas src -> styles -> clublist.css. Di sana masih terdapat satu rule styling berikut:
    1. clubdaftar {
    1.    display: block;
    1.    margintop: 32px;
    1.    width: 100%;
    1.    padding: 16px;
    1. }
Jangan hapus rule styling itu sebab kita masih memakainya untuk mengatur jarak daftar liga yang ditampilkan. Tetapi disarankan kita pindahkan rule styling itu pada berkas src -> styles -> style.css.
    1. @import “clublist.css”;
    1.  
    1. * {
    1.    padding: 0;
    1.    margin: 0;
    1.    boxsizing: borderbox;
    1. }
    1.  
    1. body {
    1.    fontfamily: sansserif;
    1. }
    1.  
    1. main {
    1.    width: 90%;
    1.    maxwidth: 800px;
    1.    margin: 32px auto;
    1. }
    1.  
    1. clubdaftar {
    1.    display: block;
    1.    margintop: 32px;
    1.    width: 100%;
    1.    padding: 16px;
    1. }
Dengan seperti itu kita bisa bebas menghapus berkas clublist.css dan menghapus @import pada berkas style.css.
20200313104746dc2f4116f225f458ab6541ca396d2223.png
Selamat! Kita telah berhasil menerapkan Shadow DOM pada seluruh custom element yang dipakai di proyek Club Finder. Sampai ketemu di materi berikutnya ya!
Langkah dari solution ini dapat Kamu temukan pula pada repository berikut: https://github.com/dicodingacademy/a163-bfwd-labs/tree/110-club-finder-shadow-dom-solution

Ebook Gratis!!

Subscribe untuk dapatkan e-book GRATIS dan informasi teknologi terbaru dan diskon menarik langsung di Email-mu

Programmer Indonesia
Programmer Indonesia
Admin yang mengelola konten khusus berita. Kalau ada yang ingin diinfokan langsung chat aja ya :D
1 1 vote
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
WhatsApp chat