Praktik Terbaik Elemen Kustom

Elemen kustom memungkinkan Anda membuat tag HTML sendiri. Checklist ini mencakup praktik terbaik untuk membantu Anda membuat elemen berkualitas tinggi.

Elemen khusus memungkinkan Anda memperluas HTML dan menentukan tag Anda sendiri. Mereka adalah fitur yang sangat kuat, tetapi mereka juga tingkat rendah, yang berarti itu tidak selalu mengetahui cara terbaik untuk mengimplementasikan elemen Anda sendiri.

Untuk membantu Anda menciptakan pengalaman terbaik, kami telah menyusun {i>checklist<i} ini. Ini menguraikan semua hal yang kita pikir diperlukan untuk menjadi elemen kustom yang berperilaku baik.

Membuat root bayangan untuk mengenkapsulasi gaya.

Mengapa demikian? Mengenkapsulasi gaya dalam shadow root elemen Anda memastikan bahwa elemen ini akan berfungsi terlepas dari tempat penggunaannya. Hal ini sangat penting jika seorang developer ingin menempatkan elemen Anda di dalam {i>shadow root<i} elemen yang lain. Ini berlaku bahkan untuk elemen sederhana seperti kotak centang atau tombol pilihan. Mungkin jika satu-satunya konten di dalam {i>shadow root<i} Anda adalah gaya itu sendiri.
Contoh Elemen <howto-checkbox>.

Buat shadow root di konstruktor.

Mengapa demikian? Konstruktor adalah saat Anda memiliki pengetahuan eksklusif tentang elemen. Inilah saat yang tepat untuk menyiapkan detail implementasi yang tidak Anda inginkan elemen yang berantakan. Melakukan pekerjaan ini di callback berikutnya, seperti connectedCallback, berarti Anda harus mencegah situasi ketika elemen Anda dilepas kemudian dipasang kembali ke dokumen.
Contoh Elemen <howto-checkbox>.

Tempatkan setiap turunan yang dibuat elemen ke dalam shadow root-nya.

Mengapa demikian? Turunan yang dibuat oleh elemen Anda adalah bagian dari implementasinya dan harus pribadi. Tanpa perlindungan {i>shadow root<i}, JavaScript luar dapat secara tidak sengaja mengganggu anak-anak ini.
Contoh Elemen <howto-tabs>.

Gunakan <slot> untuk memproyeksikan turunan light DOM ke dalam shadow DOM Anda

Mengapa demikian? Izinkan pengguna komponen Anda untuk menentukan konten dalam komponen karena turunan HTML membuat komponen Anda lebih dapat disusun. Jika browser tidak mendukung elemen kustom, konten bertingkat akan tetap tersedia, terlihat, dan dapat diakses.
Contoh Elemen <howto-tabs>.

Tetapkan gaya tampilan :host (misalnya, block, inline-block, flex) kecuali jika Anda lebih memilih default inline.

Mengapa demikian? Elemen kustom bernilai display: inline secara default. Jadi, menyetel elemen kustom width atau height tidak akan berpengaruh. Hal ini sering mengejutkan developer dan dapat menyebabkan masalah yang terkait dengan mengatur tata letak halaman. Kecuali Anda lebih memilih tampilan inline, Anda harus selalu menetapkan nilai display default.
Contoh Elemen <howto-checkbox>.

Tambahkan gaya tampilan :host yang mengikuti atribut tersembunyi.

Mengapa demikian? Elemen kustom dengan gaya display default, misalnya, :host { display: block }, akan mengganti kekhususan yang lebih rendah bawaan Atribut hidden. Anda mungkin terkejut jika ingin menyetel hidden pada elemen Anda untuk merendernya menjadi display: none. Selain itu ke gaya display default, tambahkan dukungan untuk hidden dengan :host([hidden]) { display: none }.
Contoh Elemen <howto-checkbox>.

Atribut dan properti

Jangan ganti atribut global set penulis.

Mengapa demikian? Atribut global adalah atribut yang ada pada semua elemen HTML. Agak besar contoh mencakup tabindex dan role. Elemen kustom mungkin ingin menyetel tabindex awalnya ke 0 sehingga akan menjadi keyboard dapat difokuskan. Tetapi Anda harus selalu memeriksa terlebih dahulu untuk melihat apakah pengembang menggunakan elemen Anda telah menetapkannya ke nilai lain. Misalnya, jika mereka telah menetapkan tabindex ke -1, itu merupakan sinyal bahwa mereka tidak menginginkan menjadi interaktif.
Contoh Elemen <howto-checkbox>. Hal ini dijelaskan lebih lanjut dalam Jangan ganti penulis halaman.

Selalu terima data primitif (string, angka, boolean) sebagai atribut atau properti.

Mengapa demikian? Elemen khusus, seperti komponen bawaannya, harus dapat dikonfigurasi. Konfigurasi dapat diteruskan secara deklaratif, melalui atribut, atau secara imperatif melalui properti JavaScript. Idealnya, setiap atribut juga harus ditautkan ke properti yang sesuai.
Contoh Elemen <howto-checkbox>.

Upayakan untuk menjaga atribut dan properti data primitif tetap sinkron, yang tercermin dari dari atribut ke atribut, dan sebaliknya.

Mengapa demikian? Anda tidak pernah tahu bagaimana pengguna akan berinteraksi dengan elemen Anda. Mereka mungkin menetapkan properti di JavaScript, lalu memperkirakan akan membaca nilai tersebut menggunakan API seperti getAttribute(). Jika setiap atribut memiliki properti yang sesuai, dan keduanya mencerminkan, hal itu akan memudahkan pengguna untuk bekerja dengan elemen Anda. Dengan kata lain, memanggil setAttribute('foo', value) juga harus menetapkan atribut properti foo dan sebaliknya. Tentu saja ada pengecualian untuk aturan ini. Anda tidak boleh mencerminkan properti frekuensi tinggi, mis. currentTime di pemutar video. Gunakan penilaian terbaik Anda. Jika ya terlihat seperti pengguna akan berinteraksi dengan properti atau atribut, dan tidak membebani untuk mencerminkannya, maka lakukanlah itu.
Contoh Elemen <howto-checkbox>. Hal ini dijelaskan lebih lanjut dalam Hindari masalah reentransi.

Usahakan untuk hanya menerima data lengkap (objek, array) sebagai properti.

Mengapa demikian? Secara umum, tidak ada contoh elemen HTML {i>built-in<i} yang menerima data lengkap (objek dan array JavaScript polos) melalui . Data lengkap diterima baik melalui panggilan metode atau properti baru. Ada beberapa kelemahan yang jelas dari menerima data lengkap sebagai atribut tambahan: mahal untuk membuat serialisasi objek besar ke string, dan dalam proses stringifikasi ini, referensi objek apa pun akan hilang. Sebagai misalnya, jika Anda membuat string objek yang memiliki referensi ke objek lain, atau mungkin simpul DOM, referensi itu akan hilang.

Jangan mencerminkan properti data lengkap ke atribut.

Mengapa demikian? Merefleksikan properti data lengkap ke atribut memang sangat mahal, yang memerlukan serialisasi dan deserialisasi objek JavaScript yang sama. Kecuali Anda memiliki kasus penggunaan yang hanya dapat diselesaikan dengan fitur ini, sebaiknya hindari itu.

Pertimbangkan untuk memeriksa properti yang mungkin telah ditetapkan sebelum elemen diupgrade.

Mengapa demikian? Developer yang menggunakan elemen Anda dapat mencoba menetapkan properti pada elemen tersebut sebelum definisinya dimuat. Terutama jika developer menggunakan framework yang menangani pemuatan komponen, menyegelnya ke halaman, dan mengikat propertinya ke model.
Contoh Elemen <howto-checkbox>. Dijelaskan lebih lanjut di Buat properti menjadi lambat.

Jangan menerapkan kelas secara mandiri.

Mengapa demikian? Elemen yang perlu mengekspresikan statusnya harus menggunakan atribut. Tujuan Atribut class umumnya dianggap dimiliki oleh yang menggunakan elemen Anda, dan menulisnya sendiri mungkin secara tidak sengaja menginjak kelas developer.

Acara

Mengirim peristiwa sebagai respons terhadap aktivitas komponen internal.

Mengapa demikian? Komponen Anda mungkin memiliki properti yang berubah sebagai respons terhadap aktivitas yang hanya komponen Anda yang tahu, misalnya, jika timer atau animasi selesai, atau resource selesai dimuat. Sangat membantu untuk mengirim peristiwa sebagai respons terhadap perubahan ini untuk memberi tahu {i>host<i} bahwa status komponen berbeda.

Jangan mengirim peristiwa sebagai respons terhadap setelan properti host (yang aliran data).

Mengapa demikian? Mengirim peristiwa untuk merespons setelan host properti berlebihan ({i>host<i} mengetahui keadaan saat ini karena ia baru saja mengaturnya). Mengirim peristiwa sebagai respons terhadap setelan host, properti dapat menyebabkan loop data yang tidak terbatas sistem binding.
Contoh Elemen <howto-checkbox>.

Penjelasan

Jangan ganti penulis halaman

Mungkin saja pengembang yang menggunakan elemen Anda ingin mengganti beberapa ke keadaan awal. Misalnya, mengubah role ARIA atau kemampuan fokusnya dengan tabindex. Periksa untuk melihat apakah atribut ini dan atribut global lainnya telah ditetapkan, sebelum menerapkan nilai Anda sendiri.

connectedCallback() {
  if (!this.hasAttribute('role'))
    this.setAttribute('role', 'checkbox');
  if (!this.hasAttribute('tabindex'))
    this.setAttribute('tabindex', 0);

Membuat properti menjadi lambat

Pengembang mungkin mencoba untuk menetapkan properti pada elemen Anda sebelum telah dimuat. Terutama jika pengembang menggunakan yang menangani pemuatan komponen, memasukkannya ke dalam halaman, dan mengikat propertinya ke model.

Pada contoh berikut, Angular secara deklaratif mengikat elemen isChecked ke properti checked kotak centang. Jika definisi untuk kotak centang petunjuk dimuat dengan lambat, mungkin saja Angular mungkin mencoba menetapkan properti yang dicentang sebelum elemen diupgrade.

<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>

Elemen khusus harus menangani skenario ini dengan memeriksa apakah ada properti yang sudah ditetapkan pada instance-nya. <howto-checkbox> menunjukkan pola ini menggunakan metode yang disebut _upgradeProperty().

connectedCallback() {
  ...
  this._upgradeProperty('checked');
}

_upgradeProperty(prop) {
  if (this.hasOwnProperty(prop)) {
    let value = this[prop];
    delete this[prop];
    this[prop] = value;
  }
}

_upgradeProperty() mengambil nilai dari instance yang belum diupgrade dan menghapusnya properti agar tidak membayangi penyetel properti elemen kustom itu sendiri. Dengan cara ini, ketika definisi elemen akhirnya dimuat, ia bisa segera menunjukkan keadaan yang benar.

Menghindari masalah reentransi

Anda mungkin ingin menggunakan attributeChangedCallback() untuk mencerminkan status ke properti pokok, misalnya:

// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'checked')
    this.checked = newValue;
}

Namun, hal ini bisa menimbulkan loop tak terbatas jika penyetel properti juga mencerminkan atribut ini.

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    // OOPS! This will cause an infinite loop because it triggers the
    // attributeChangedCallback() which then sets this property again.
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

Alternatifnya adalah dengan memungkinkan penyetel properti untuk tercermin ke atribut, dan meminta pengambil menentukan nilainya berdasarkan atribut.

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

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

Dalam contoh ini, menambahkan atau menghapus atribut juga akan menetapkan properti.

Terakhir, attributeChangedCallback() dapat digunakan untuk menangani efek samping seperti menerapkan status ARIA.

attributeChangedCallback(name, oldValue, newValue) {
  const hasValue = newValue !== null;
  switch (name) {
    case 'checked':
      // Note the attributeChangedCallback is only handling the *side effects*
      // of setting the attribute.
      this.setAttribute('aria-checked', hasValue);
      break;
    ...
  }
}