Elemen Khusus v1 - Komponen Web yang Dapat Digunakan Kembali

Elemen kustom memungkinkan developer web menentukan tag HTML baru, memperluas tag yang ada, dan membuat komponen web yang dapat digunakan kembali.

Dengan Elemen Kustom, developer web dapat membuat tag HTML baru, meningkatkan kualitas tag HTML yang ada, atau memperluas komponen yang telah ditulis oleh developer lain. API ini adalah fondasi komponen web. Library ini menghadirkan cara berbasis standar web untuk membuat komponen yang dapat digunakan kembali hanya dengan menggunakan JS/HTML/CSS vanilla. Hasilnya adalah lebih sedikit kode, kode modular, dan lebih banyak penggunaan kembali dalam aplikasi kita.

Pengantar

Browser memberi kita alat yang sangat baik untuk menyusun aplikasi web. Ini disebut HTML. Anda mungkin pernah mendengarnya. API ini bersifat deklaratif, portabel, didukung dengan baik, dan mudah digunakan. Meskipun HTML sangat bagus, kosakata dan ekstensibilitasnya terbatas. Standar aktif HTML selalu tidak memiliki cara untuk secara otomatis mengaitkan perilaku JS dengan markup Anda… hingga sekarang.

Elemen kustom adalah jawaban untuk memodernisasi HTML, mengisi bagian yang hilang, dan memaketkan struktur dengan perilaku. Jika HTML tidak memberikan solusi untuk suatu masalah, kita dapat membuat elemen kustom yang memberikan solusi. Elemen kustom mengajarkan trik baru kepada browser sekaligus mempertahankan manfaat HTML.

Menentukan elemen baru

Untuk menentukan elemen HTML baru, kita memerlukan kemampuan JavaScript.

customElements global digunakan untuk menentukan elemen kustom dan mengajar browser tentang tag baru. Panggil customElements.define() dengan nama tag yang ingin Anda buat dan class JavaScript yang memperluas HTMLElement dasar.

Contoh - menentukan panel panel samping seluler, <app-drawer>:

class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);

// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});

Contoh penggunaan:

<app-drawer></app-drawer>

Perlu diingat bahwa menggunakan elemen kustom tidak berbeda dengan menggunakan <div> atau elemen lainnya. Instance dapat dideklarasikan di halaman, dibuat secara dinamis di JavaScript, pemroses peristiwa dapat dilampirkan, dll. Lanjutkan membaca untuk mengetahui contoh lainnya.

Menentukan JavaScript API elemen

Fungsi elemen kustom ditentukan menggunakan class ES2015 yang memperluas HTMLElement. Memperluas HTMLElement memastikan elemen kustom mewarisi seluruh DOM API dan berarti properti/metode apa pun yang Anda tambahkan ke class menjadi bagian dari antarmuka DOM elemen. Pada dasarnya, gunakan class untuk membuat JavaScript API publik untuk tag Anda.

Contoh - menentukan antarmuka DOM <app-drawer>:

class AppDrawer extends HTMLElement {

  // A getter/setter for an open property.
  get open() {
    return this.hasAttribute('open');
  }

  set open(val) {
    // Reflect the value of the open property as an HTML attribute.
    if (val) {
      this.setAttribute('open', '');
    } else {
      this.removeAttribute('open');
    }
    this.toggleDrawer();
  }

  // A getter/setter for a disabled property.
  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    // Reflect the value of the disabled property as an HTML attribute.
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Can define constructor arguments if you wish.
  constructor() {
    // If you define a constructor, always call super() first!
    // This is specific to CE and required by the spec.
    super();

    // Setup a click listener on <app-drawer> itself.
    this.addEventListener('click', e => {
      // Don't toggle the drawer if it's disabled.
      if (this.disabled) {
        return;
      }
      this.toggleDrawer();
    });
  }

  toggleDrawer() {
    // ...
  }
}

customElements.define('app-drawer', AppDrawer);

Dalam contoh ini, kita membuat panel samping yang memiliki properti open, properti disabled, dan metode toggleDrawer(). Fungsi ini juga mencerminkan properti sebagai atribut HTML.

Fitur menarik dari elemen kustom adalah this di dalam definisi class mengacu pada elemen DOM itu sendiri, yaitu instance class. Dalam contoh kami, this merujuk ke <app-drawer>. Ini (😉) adalah cara elemen dapat melampirkan pemroses click ke dirinya sendiri. Selain itu, Anda tidak terbatas pada pemroses peristiwa. Seluruh DOM API tersedia di dalam kode elemen. Gunakan this untuk mengakses properti elemen, memeriksa turunannya (this.children), membuat kueri node (this.querySelectorAll('.items')), dll.

Aturan tentang cara membuat elemen kustom

  1. Nama elemen kustom harus berisi tanda hubung (-). Jadi, <x-tags>, <my-element>, dan <my-awesome-app> adalah nama yang valid, sedangkan <tabs> dan <foo_bar> tidak. Persyaratan ini agar parser HTML dapat membedakan elemen kustom dari elemen reguler. Hal ini juga memastikan kompatibilitas mendatang saat tag baru ditambahkan ke HTML.
  2. Anda tidak dapat mendaftarkan tag yang sama lebih dari sekali. Jika dilakukan, akan muncul DOMException. Setelah memberi tahu browser tentang tag baru, Anda sudah selesai. Tidak ada pengembalian.
  3. Elemen kustom tidak dapat ditutup sendiri karena HTML hanya mengizinkan beberapa elemen ditutup sendiri. Selalu tulis tag penutup (<app-drawer></app-drawer>).

Reaksi elemen kustom

Elemen kustom dapat menentukan hook siklus proses khusus untuk menjalankan kode selama waktu keberadaannya yang menarik. Ini disebut reaksi elemen kustom.

Nama Dipanggil saat
constructor Instance elemen dibuat atau diupgrade. Berguna untuk menginisialisasi status, menyiapkan pemroses peristiwa, atau membuat shadow dom. Lihat spesifikasi untuk mengetahui batasan pada hal yang dapat Anda lakukan di constructor.
connectedCallback Dipanggil setiap kali elemen disisipkan ke dalam DOM. Berguna untuk menjalankan kode penyiapan, seperti mengambil resource atau merender. Secara umum, Anda harus mencoba menunda pekerjaan hingga waktu ini.
disconnectedCallback Dipanggil setiap kali elemen dihapus dari DOM. Berguna untuk menjalankan kode pembersihan.
attributeChangedCallback(attrName, oldVal, newVal) Dipanggil saat atribut yang diamati telah ditambahkan, dihapus, diperbarui, atau diganti. Juga dipanggil untuk nilai awal saat elemen dibuat oleh parser, atau diupgrade. Catatan: hanya atribut yang tercantum dalam properti observedAttributes yang akan menerima callback ini.
adoptedCallback Elemen kustom telah dipindahkan ke document baru (misalnya, seseorang yang disebut document.adoptNode(el)).

Callback reaksi bersifat sinkron. Jika seseorang memanggil el.setAttribute() pada elemen Anda, browser akan segera memanggil attributeChangedCallback(). Demikian pula, Anda akan menerima disconnectedCallback() tepat setelah elemen Anda dihapus dari DOM (misalnya, pengguna memanggil el.remove()).

Contoh: menambahkan reaksi elemen kustom ke <app-drawer>:

class AppDrawer extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.
    // ...
  }

  connectedCallback() {
    // ...
  }

  disconnectedCallback() {
    // ...
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    // ...
  }
}

Tentukan reaksi jika/saat reaksi tersebut masuk akal. Jika elemen Anda cukup kompleks dan membuka koneksi ke IndexedDB di connectedCallback(), lakukan pekerjaan pembersihan yang diperlukan di disconnectedCallback(). Namun, berhati-hatilah. Anda tidak dapat mengandalkan elemen yang dihapus dari DOM dalam semua situasi. Misalnya, disconnectedCallback() tidak akan pernah dipanggil jika pengguna menutup tab.

Properti dan atribut

Mencerminkan properti ke atribut

Properti HTML biasanya mencerminkan nilainya kembali ke DOM sebagai atribut HTML. Misalnya, saat nilai hidden atau id diubah di JS:

div.id = 'my-id';
div.hidden = true;

nilai diterapkan ke DOM aktif sebagai atribut:

<div id="my-id" hidden>

Hal ini disebut "mencerminkan properti ke atribut". Hampir setiap properti di HTML melakukan hal ini. Mengapa? Atribut juga berguna untuk mengonfigurasi elemen secara deklaratif dan API tertentu seperti aksesibilitas dan pemilih CSS mengandalkan atribut untuk berfungsi.

Mencerminkan properti berguna di mana pun Anda ingin menjaga representasi DOM elemen sinkron dengan status JavaScript-nya. Salah satu alasan Anda mungkin ingin mencerminkan properti adalah agar gaya yang ditentukan pengguna diterapkan saat status JS berubah.

Ingat kembali <app-drawer> kami. Pengguna komponen ini mungkin ingin memudarkannya dan/atau mencegah interaksi pengguna saat dinonaktifkan:

app-drawer[disabled] {
  opacity: 0.5;
  pointer-events: none;
}

Saat properti disabled diubah di JS, kita ingin atribut tersebut ditambahkan ke DOM sehingga pemilih pengguna cocok. Elemen dapat memberikan perilaku tersebut dengan mencerminkan nilai ke atribut dengan nama yang sama:

get disabled() {
  return this.hasAttribute('disabled');
}

set disabled(val) {
  // Reflect the value of `disabled` as an attribute.
  if (val) {
    this.setAttribute('disabled', '');
  } else {
    this.removeAttribute('disabled');
  }
  this.toggleDrawer();
}

Mengamati perubahan pada atribut

Atribut HTML adalah cara mudah bagi pengguna untuk mendeklarasikan status awal:

<app-drawer open disabled></app-drawer>

Elemen dapat bereaksi terhadap perubahan atribut dengan menentukan attributeChangedCallback. Browser akan memanggil metode ini untuk setiap perubahan pada atribut yang tercantum dalam array observedAttributes.

class AppDrawer extends HTMLElement {
  // ...

  static get observedAttributes() {
    return ['disabled', 'open'];
  }

  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Only called for the disabled and open attributes due to observedAttributes
  attributeChangedCallback(name, oldValue, newValue) {
    // When the drawer is disabled, update keyboard/screen reader behavior.
    if (this.disabled) {
      this.setAttribute('tabindex', '-1');
      this.setAttribute('aria-disabled', 'true');
    } else {
      this.setAttribute('tabindex', '0');
      this.setAttribute('aria-disabled', 'false');
    }
    // TODO: also react to the open attribute changing.
  }
}

Dalam contoh ini, kita menetapkan atribut tambahan pada <app-drawer> saat atribut disabled diubah. Meskipun kita tidak melakukannya di sini, Anda juga dapat menggunakan attributeChangedCallback untuk memastikan properti JS tetap sinkron dengan atributnya.

Upgrade elemen

HTML yang ditingkatkan secara progresif

Kita telah mempelajari bahwa elemen kustom ditentukan dengan memanggil customElements.define(). Namun, ini bukan berarti Anda harus menentukan + mendaftarkan elemen kustom sekaligus.

Elemen kustom dapat digunakan sebelum definisinya didaftarkan.

Progressive enhancement adalah fitur elemen kustom. Dengan kata lain, Anda dapat mendeklarasikan banyak elemen <app-drawer> di halaman dan tidak pernah memanggil customElements.define('app-drawer', ...) hingga nanti. Hal ini karena browser memperlakukan potensi elemen kustom secara berbeda berkat tag tidak dikenal. Proses memanggil define() dan memberi elemen yang ada dengan definisi class disebut "upgrade elemen".

Untuk mengetahui kapan nama tag ditentukan, Anda dapat menggunakan window.customElements.whenDefined(). Fungsi ini menampilkan Promise yang di-resolve saat elemen ditentukan.

customElements.whenDefined('app-drawer').then(() => {
  console.log('app-drawer defined');
});

Contoh - menunda pekerjaan hingga sekumpulan elemen turunan diupgrade

<share-buttons>
  <social-button type="twitter"><a href="...">Twitter</a></social-button>
  <social-button type="fb"><a href="...">Facebook</a></social-button>
  <social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>
// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');

let promises = [...undefinedButtons].map((socialButton) => {
  return customElements.whenDefined(socialButton.localName);
});

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});

Konten yang ditentukan elemen

Elemen kustom dapat mengelola kontennya sendiri menggunakan DOM API di dalam kode elemen. Reaksi sangat berguna untuk hal ini.

Contoh - buat elemen dengan beberapa HTML default:

customElements.define('x-foo-with-markup', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
  }
  // ...
});

Mendeklarasikan tag ini akan menghasilkan:

<x-foo-with-markup>
  <b>I'm an x-foo-with-markup!</b>
</x-foo-with-markup>

// TODO: DevSite - Contoh kode dihapus karena menggunakan pengendali peristiwa inline

Membuat elemen yang menggunakan Shadow DOM

Shadow DOM menyediakan cara bagi elemen untuk memiliki, merender, dan menata gaya bagian DOM yang terpisah dari bagian halaman lainnya. Bahkan, Anda dapat menyembunyikan seluruh aplikasi dalam satu tag:

<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
<chat-app></chat-app>

Untuk menggunakan Shadow DOM dalam elemen kustom, panggil this.attachShadow di dalam constructor:

let tmpl = document.createElement('template');
tmpl.innerHTML = `
  <style>:host { ... }</style> <!-- look ma, scoped styles -->
  <b>I'm in shadow dom!</b>
  <slot></slot>
`;

customElements.define('x-foo-shadowdom', class extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.

    // Attach a shadow root to the element.
    let shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.appendChild(tmpl.content.cloneNode(true));
  }
  // ...
});

Contoh penggunaan:

<x-foo-shadowdom>
  <p><b>User's</b> custom text</p>
</x-foo-shadowdom>

<!-- renders as -->
<x-foo-shadowdom>
  #shadow-root
  <b>I'm in shadow dom!</b>
  <slot></slot> <!-- slotted content appears here -->
</x-foo-shadowdom>

Teks kustom pengguna

// TODO: DevSite - Contoh kode dihapus karena menggunakan pengendali peristiwa inline

Membuat elemen dari <template>

Bagi yang belum mengetahuinya, elemen <template> memungkinkan Anda mendeklarasikan fragmen DOM yang diuraikan, tidak aktif saat pemuatan halaman, dan dapat diaktifkan nanti saat runtime. Ini adalah primitif API lain dalam keluarga komponen web. Template adalah placeholder yang ideal untuk mendeklarasikan struktur elemen kustom.

Contoh: mendaftarkan elemen dengan konten Shadow DOM yang dibuat dari <template>:

<template id="x-foo-from-template">
  <style>
    p { color: green; }
  </style>
  <p>I'm in Shadow DOM. My markup was stamped from a &lt;template&gt;.</p>
</template>

<script>
  let tmpl = document.querySelector('#x-foo-from-template');
  // If your code is inside of an HTML Import you'll need to change the above line to:
  // let tmpl = document.currentScript.ownerDocument.querySelector('#x-foo-from-template');

  customElements.define('x-foo-from-template', class extends HTMLElement {
    constructor() {
      super(); // always call super() first in the constructor.
      let shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(tmpl.content.cloneNode(true));
    }
    // ...
  });
</script>

Beberapa baris kode ini sangat efektif. Mari kita pahami hal-hal penting yang terjadi:

  1. Kita menentukan elemen baru di HTML: <x-foo-from-template>
  2. Shadow DOM elemen dibuat dari <template>
  3. DOM elemen bersifat lokal untuk elemen berkat DOM Bayangan
  4. CSS internal elemen dicakup ke elemen berkat DOM Bayangan

Saya berada di Shadow DOM. Markup saya dicap dari <template>.

// TODO: DevSite - Contoh kode dihapus karena menggunakan pengendali peristiwa inline

Menata gaya elemen kustom

Meskipun elemen Anda menentukan gayanya sendiri menggunakan Shadow DOM, pengguna dapat menata gaya elemen kustom Anda dari halaman mereka. Hal ini disebut "gaya yang ditentukan pengguna".

<!-- user-defined styling -->
<style>
  app-drawer {
    display: flex;
  }
  panel-item {
    transition: opacity 400ms ease-in-out;
    opacity: 0.3;
    flex: 1;
    text-align: center;
    border-radius: 50%;
  }
  panel-item:hover {
    opacity: 1.0;
    background: rgb(255, 0, 255);
    color: white;
  }
  app-panel > panel-item {
    padding: 5px;
    list-style: none;
    margin: 0 7px;
  }
</style>

<app-drawer>
  <panel-item>Do</panel-item>
  <panel-item>Re</panel-item>
  <panel-item>Mi</panel-item>
</app-drawer>

Anda mungkin bertanya-tanya bagaimana cara kerja kekhususan CSS jika elemen memiliki gaya yang ditentukan dalam Shadow DOM. Dalam hal kekhususan, gaya pengguna lebih unggul. Gaya ini akan selalu mengganti gaya yang ditentukan elemen. Lihat bagian tentang Membuat elemen yang menggunakan Shadow DOM.

Menata gaya elemen yang tidak terdaftar

Sebelum elemen diupgrade, Anda dapat menargetkannya di CSS menggunakan pseudo-class :defined. Hal ini berguna untuk menata gaya komponen sebelumnya. Misalnya, Anda mungkin ingin mencegah tata letak atau FOUC visual lainnya dengan menyembunyikan komponen yang tidak ditentukan dan memudarkannya saat ditentukan.

Contoh - sembunyikan <app-drawer> sebelum ditentukan:

app-drawer:not(:defined) {
  /* Pre-style, give layout, replicate app-drawer's eventual styles, etc. */
  display: inline-block;
  height: 100vh;
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

Setelah <app-drawer> ditentukan, pemilih (app-drawer:not(:defined)) tidak lagi cocok.

Memperluas elemen

Custom Elements API berguna untuk membuat elemen HTML baru, tetapi juga berguna untuk memperluas elemen kustom lainnya atau bahkan HTML bawaan browser.

Memperluas elemen kustom

Memperluas elemen kustom lain dilakukan dengan memperluas definisi class-nya.

Contoh - membuat <fancy-app-drawer> yang memperluas <app-drawer>:

class FancyDrawer extends AppDrawer {
  constructor() {
    super(); // always call super() first in the constructor. This also calls the extended class' constructor.
    // ...
  }

  toggleDrawer() {
    // Possibly different toggle implementation?
    // Use ES2015 if you need to call the parent method.
    // super.toggleDrawer()
  }

  anotherMethod() {
    // ...
  }
}

customElements.define('fancy-app-drawer', FancyDrawer);

Memperluas elemen HTML native

Misalnya, Anda ingin membuat <button> yang lebih menarik. Daripada mereplikasi perilaku dan fungsi <button>, opsi yang lebih baik adalah meningkatkan elemen yang ada secara bertahap menggunakan elemen kustom.

Elemen bawaan yang disesuaikan adalah elemen kustom yang memperluas salah satu tag HTML bawaan browser. Manfaat utama memperluas elemen yang ada adalah untuk mendapatkan semua fiturnya (properti DOM, metode, aksesibilitas). Tidak ada cara yang lebih baik untuk menulis aplikasi web progresif daripada meningkatkan elemen HTML yang ada secara progresif.

Untuk memperluas elemen, Anda harus membuat definisi class yang diwarisi dari antarmuka DOM yang benar. Misalnya, elemen kustom yang memperluas <button> harus mewarisi dari HTMLButtonElement, bukan HTMLElement. Demikian pula, elemen yang memperluas <img> harus memperluas HTMLImageElement.

Contoh - memperluas <button>:

// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class FancyButton extends HTMLButtonElement {
  constructor() {
    super(); // always call super() first in the constructor.
    this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
  }

  // Material design ripple animation.
  drawRipple(x, y) {
    let div = document.createElement('div');
    div.classList.add('ripple');
    this.appendChild(div);
    div.style.top = `${y - div.clientHeight/2}px`;
    div.style.left = `${x - div.clientWidth/2}px`;
    div.style.backgroundColor = 'currentColor';
    div.classList.add('run');
    div.addEventListener('transitionend', (e) => div.remove());
  }
}

customElements.define('fancy-button', FancyButton, {extends: 'button'});

Perhatikan bahwa panggilan ke define() sedikit berubah saat memperluas elemen native. Parameter ketiga yang diperlukan memberi tahu browser tag mana yang Anda perluas. Hal ini diperlukan karena banyak tag HTML memiliki antarmuka DOM yang sama. <section>, <address>, dan <em> (di antara yang lainnya) semuanya memiliki HTMLElement yang sama; <q> dan <blockquote> memiliki HTMLQuoteElement yang sama; dll. Dengan menentukan {extends: 'blockquote'}, browser akan mengetahui bahwa Anda membuat <blockquote> yang ditingkatkan, bukan <q>. Lihat spesifikasi HTML untuk mengetahui daftar lengkap antarmuka DOM HTML.

Pengguna elemen bawaan yang disesuaikan dapat menggunakannya dengan beberapa cara. Mereka dapat mendeklarasikannya dengan menambahkan atribut is="" pada tag native:

<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>

membuat instance di JavaScript:

// Custom elements overload createElement() to support the is="" attribute.
let button = document.createElement('button', {is: 'fancy-button'});
button.textContent = 'Fancy button!';
button.disabled = true;
document.body.appendChild(button);

atau gunakan operator new:

let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;

Berikut adalah contoh lain yang memperluas <img>.

Contoh - memperluas <img>:

customElements.define('bigger-img', class extends Image {
  // Give img default size if users don't specify.
  constructor(width=50, height=50) {
    super(width * 10, height * 10);
  }
}, {extends: 'img'});

Pengguna mendeklarasikan komponen ini sebagai:

<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">

atau buat instance di JavaScript:

const BiggerImage = customElements.get('bigger-img');
const image = new BiggerImage(15, 20); // pass constructor values like so.
console.assert(image.width === 150);
console.assert(image.height === 200);

Detail lainnya

Elemen tidak dikenal vs. elemen kustom yang tidak ditentukan

HTML bersifat longgar dan fleksibel untuk digunakan. Misalnya, deklarasikan <randomtagthatdoesntexist> di halaman dan browser akan dengan senang hati menerimanya. Mengapa tag non-standar berfungsi? Jawabannya adalah spesifikasi HTML mengizinkannya. Elemen yang tidak ditentukan oleh spesifikasi akan diuraikan sebagai HTMLUnknownElement.

Hal yang sama tidak berlaku untuk elemen kustom. Elemen kustom potensial diuraikan sebagai HTMLElement jika dibuat dengan nama yang valid (termasuk "-"). Anda dapat memeriksanya di browser yang mendukung elemen kustom. Aktifkan Konsol: Ctrl+Shift+J (atau Cmd+Opt+J di Mac) dan tempelkan baris kode berikut:

// "tabs" is not a valid custom element name
document.createElement('tabs') instanceof HTMLUnknownElement === true

// "x-tabs" is a valid custom element name
document.createElement('x-tabs') instanceof HTMLElement === true

Referensi API

Global customElements menentukan metode yang berguna untuk menggunakan elemen kustom.

define(tagName, constructor, options)

Menentukan elemen kustom baru di browser.

Contoh

customElements.define('my-app', class extends HTMLElement { ... });
customElements.define(
    'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});

get(tagName)

Dengan nama tag elemen kustom yang valid, menampilkan konstruktor elemen. Menampilkan undefined jika tidak ada definisi elemen yang terdaftar.

Contoh

let Drawer = customElements.get('app-drawer');
let drawer = new Drawer();

whenDefined(tagName)

Menampilkan Promise yang di-resolve saat elemen kustom ditentukan. Jika elemen sudah ditentukan, segera selesaikan. Menolak jika nama tag bukan nama elemen kustom yang valid.

Contoh

customElements.whenDefined('app-drawer').then(() => {
  console.log('ready!');
});

Dukungan histori dan browser

Jika telah mengikuti komponen web selama beberapa tahun terakhir, Anda akan mengetahui bahwa Chrome 36+ menerapkan versi Custom Elements API yang menggunakan document.registerElement(), bukan customElements.define(). Versi tersebut kini dianggap sebagai versi standar yang tidak digunakan lagi, yang disebut v0. customElements.define() adalah hal baru yang sedang populer dan mulai diterapkan oleh vendor browser. Versi ini disebut Elemen Kustom v1.

Jika Anda tertarik dengan spesifikasi v0 lama, lihat artikel html5rocks.

Dukungan browser

Chrome 54 (status), Safari 10.1 (status), dan Firefox 63 (status) memiliki Elemen Kustom v1. Edge telah memulai pengembangan.

Untuk mendeteksi elemen kustom, periksa keberadaan window.customElements:

const supportsCustomElementsV1 = 'customElements' in window;

Polyfill

Hingga dukungan browser tersedia secara luas, ada polyfill mandiri yang tersedia untuk Elemen Kustom v1. Namun, sebaiknya gunakan loader webcomponents.js untuk memuat polyfill komponen web secara optimal. Loader menggunakan deteksi fitur untuk memuat secara asinkron hanya pollyfill yang diperlukan yang diperlukan oleh browser.

Instal:

npm install --save @webcomponents/webcomponentsjs

Penggunaan:

<!-- Use the custom element on the page. -->
<my-element></my-element>

<!-- Load polyfills; note that "loader" will load these async -->
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js" defer></script>

<!-- Load a custom element definitions in `waitFor` and return a promise -->
<script type="module">
  function loadScript(src) {
    return new Promise(function(resolve, reject) {
      const script = document.createElement('script');
      script.src = src;
      script.onload = resolve;
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }

  WebComponents.waitFor(() => {
    // At this point we are guaranteed that all required polyfills have
    // loaded, and can use web components APIs.
    // Next, load element definitions that call `customElements.define`.
    // Note: returning a promise causes the custom elements
    // polyfill to wait until all definitions are loaded and then upgrade
    // the document in one batch, for better performance.
    return loadScript('my-element.js');
  });
</script>

Kesimpulan

Elemen kustom memberi kita alat baru untuk menentukan tag HTML baru di browser dan membuat komponen yang dapat digunakan kembali. Gabungkan dengan primitif platform baru lainnya seperti Shadow DOM dan <template>, dan kita mulai menyadari gambaran besar Komponen Web:

  • Lintas browser (standar web) untuk membuat dan memperluas komponen yang dapat digunakan kembali.
  • Tidak memerlukan library atau framework untuk memulai. Vanilla JS/HTML FTW!
  • Memberikan model pemrograman yang sudah dikenal. Hanya DOM/CSS/HTML.
  • Berfungsi dengan baik dengan fitur platform web baru lainnya (Shadow DOM, <template>, properti khusus CSS, dll.)
  • Terintegrasi erat dengan DevTools browser.
  • Manfaatkan fitur aksesibilitas yang ada.