Biz oâz klassimiz bilan tasvirlangan custom HTML elementlarni yaratishimiz mumkin, oâz metodlari va xususiyatlari, hodisalari va boshqalar bilan.
Custom element aniqlangandan soâng, biz uni oârnatilgan HTML elementlar bilan teng darajada ishlatishimiz mumkin.
Bu ajoyib, chunki HTML lugâati boy, lekin cheksiz emas. <easy-tabs>, <sliding-carousel>, <beautiful-upload> elementlari yoâq⦠Bizga kerak boâlishi mumkin boâlgan boshqa har qanday tegni oâylab koâring.
Biz ularni maxsus klass bilan aniqlay olamiz va keyin ular har doim HTML ning bir qismi boâlgandek ishlatamiz.
Custom elementlarning ikki turi mavjud:
- Avtonom custom elementlar â âmutlaqo yangiâ elementlar, abstrakt
HTMLElementklassini kengaytiradi. - Moslashtirilgan oârnatilgan elementlar â oârnatilgan elementlarni kengaytiradi, masalan
HTMLButtonElementasosidagi moslashtirilgan tugma va boshqalar.
Avval avtonom elementlarni koârib chiqamiz, keyin moslashtirilgan oârnatilganlarga oâtamiz.
Custom element yaratish uchun biz brauzerga u haqida bir nechta tafsilotlarni aytishimiz kerak: uni qanday koârsatish kerak, element sahifaga qoâshilganda yoki olib tashlaganda nima qilish kerak va hokazo.
Bu maxsus metodlar bilan klass yaratish orqali amalga oshiriladi. Bu oson, chunki metodlar kam va ularning barchasi ixtiyoriy.
Mana toâliq roâyxat bilan eskiz:
class MyElement extends HTMLElement {
constructor() {
super();
// element yaratildi
}
connectedCallback() {
// element hujjatga qo'shilganda brauzer bu metodini chaqiradi
// (agar element qayta-qayta qo'shilsa/olib tashlansa, ko'p marta chaqirilishi mumkin)
}
disconnectedCallback() {
// element hujjatdan olib tashlaganda brauzer bu metodini chaqiradi
// (agar element qayta-qayta qo'shilsa/olib tashlansa, ko'p marta chaqirilishi mumkin)
}
static get observedAttributes() {
return [/* o'zgarishlarni kuzatish uchun atribut nomlari massivi */];
}
attributeChangedCallback(name, oldValue, newValue) {
// yuqorida sanab o'tilgan atributlardan biri o'zgartirilganda chaqiriladi
}
adoptedCallback() {
// element yangi hujjatga ko'chirilganda chaqiriladi
// (document.adoptNode da sodir bo'ladi, juda kamdan-kam ishlatiladi)
}
// boshqa element metodlari va xususiyatlari bo'lishi mumkin
}
Shundan soâng, elementni roâyxatdan oâtkazishimiz kerak:
// brauzerga <my-element> bizning yangi klassimiz tomonidan xizmat qilinishini bildiring
customElements.define("my-element", MyElement);
Endi <my-element> tegi bilan har qanday HTML elementlari uchun MyElementning nusxasi yaratiladi va yuqorida aytilgan metodlar chaqiriladi. Shuningdek, biz JavaScript da document.createElement('my-element') ni chaqirishimiz mumkin.
- ni oâz ichiga olishi kerakCustom element nomi tire -ni oâz ichiga olishi kerak, masalan my-element va super-button toâgâri nomlar, lekin myelement emas.
Bu oârnatilgan va custom HTML elementlar oârtasida nom toâqnashuvlari yoâqligini taâminlash uchun.
Misol: âtime-formattedâ
Masalan, HTML da sana/vaqt uchun <time> elementi allaqachon mavjud. Lekin u oâzi hech qanday formatlashni qilmaydi.
Keling, vaqtni chiroyli, tilga mos formatda koârsatadigan <time-formatted> elementini yarataylik:
<script>
class TimeFormatted extends HTMLElement { // (1)
connectedCallback() {
let date = new Date(this.getAttribute('datetime') || Date.now());
this.innerHTML = new Intl.DateTimeFormat("default", {
year: this.getAttribute('year') || undefined,
month: this.getAttribute('month') || undefined,
day: this.getAttribute('day') || undefined,
hour: this.getAttribute('hour') || undefined,
minute: this.getAttribute('minute') || undefined,
second: this.getAttribute('second') || undefined,
timeZoneName: this.getAttribute('time-zone-name') || undefined,
}).format(date);
}
}
customElements.define("time-formatted", TimeFormatted); // (2)
</script>
<!-- (3) -->
<time-formatted datetime="2019-12-01"
year="numeric" month="long" day="numeric"
hour="numeric" minute="numeric" second="numeric"
time-zone-name="short"
></time-formatted>
- Klassda faqat bitta
connectedCallback()metodi bor â<time-formatted>elementi sahifaga qoâshilganda (yoki HTML parser uni aniqlayotganda) brauzer uni chaqiradi va chiroyli formatlangan vaqtni koârsatish uchun brauzerlarda yaxshi qoâllab-quvvatlanadigan oârnatilgan Intl.DateTimeFormat maâlumot formatlovchisidan foydalanadi. - Biz yangi elementimizni
customElements.define(tag, class)orqali roâyxatdan oâtkazishimiz kerak. - Va keyin uni hamma joyda ishlatishimiz mumkin.
Agar brauzer customElements.define dan oldin <time-formatted> elementlarga duch kelsa, bu xato emas. Lekin element hali nomaâlum, har qanday nostandart teg kabi.
Bunday âaniqlanmaganâ elementlarni CSS selektori :not(:defined) bilan shakllantirish mumkin.
customElement.define chaqirilganda, ular âyangilanadiâ: har biri uchun TimeFormatted ning yangi nusxasi yaratiladi va connectedCallback chaqiriladi. Ular :defined boâladi.
Custom elementlar haqida maâlumot olish uchun metodlar mavjud:
customElements.get(name)â berilgannamebilan custom element uchun klassni qaytaradi,customElements.whenDefined(name)â berilgannamebilan custom element aniqlanganda hal qilinadigan promise qaytaradi (qiymatsiz).
connectedCallback da render qilish, constructor da emasYuqoridagi misolda element mazmuni connectedCallback da render qilinadi (yaratiladi).
Nima uchun constructor da emas?
Sabab oddiy: constructor chaqirilganda, hali juda erta. Element yaratilgan, lekin brauzer hali bu bosqichda atributlarni qayta ishlamagan/tayinlamagan: getAttribute ga chaqiruvlar null qaytaradi. Shuning uchun biz u yerda haqiqatan ham render qila olmaymiz.
Bundan tashqari, agar bu haqida oâylasangiz, bu ishlash nuqtai nazaridan yaxshiroq â ishni haqiqatan ham kerak boâlgunga qadar kechiktirish.
connectedCallback element hujjatga qoâshilganda ishga tushadi. Boshqa elementga farzand sifatida qoâshilgandagina emas, balki haqiqatan ham sahifaning bir qismiga aylanganida. Shunday qilib, biz ajratilgan DOM qurish, elementlar yaratish va ularni keyingi foydalanish uchun tayyorlashimiz mumkin. Ular faqat sahifaga kirganda haqiqatan ham render qilinadi.
Atributlarni kuzatish
<time-formatted> ning joriy amalga oshirilishida element render qilingandan keyin atributlarning keyingi oâzgarishlari hech qanday taâsir koârsatmaydi. Bu HTML element uchun gâalati. Odatda, a.href kabi atributni oâzgartirganimizda, oâzgarish darhol koârinadigan boâlishini kutamiz. Keling, buni tuzataylik.
Biz ularning roâyxatini observedAttributes() statik getter da taqdim etish orqali atributlarni kuzatishimiz mumkin. Bunday atributlar uchun ular oâzgartirilganda attributeChangedCallback chaqiriladi. Bu boshqa, roâyxatga kiritilmagan atributlar uchun ishga tushmaydi (bu ishlash sabablari uchun).
Mana atributlar oâzgartirilganda avtomatik yangilanadigan yangi <time-formatted>:
<script>
class TimeFormatted extends HTMLElement {
render() { // (1)
let date = new Date(this.getAttribute('datetime') || Date.now());
this.innerHTML = new Intl.DateTimeFormat("default", {
year: this.getAttribute('year') || undefined,
month: this.getAttribute('month') || undefined,
day: this.getAttribute('day') || undefined,
hour: this.getAttribute('hour') || undefined,
minute: this.getAttribute('minute') || undefined,
second: this.getAttribute('second') || undefined,
timeZoneName: this.getAttribute('time-zone-name') || undefined,
}).format(date);
}
connectedCallback() { // (2)
if (!this.rendered) {
this.render();
this.rendered = true;
}
}
static get observedAttributes() { // (3)
return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];
}
attributeChangedCallback(name, oldValue, newValue) { // (4)
this.render();
}
}
customElements.define("time-formatted", TimeFormatted);
</script>
<time-formatted id="elem" hour="numeric" minute="numeric" second="numeric"></time-formatted>
<script>
setInterval(() => elem.setAttribute('datetime', new Date()), 1000); // (5)
</script>
- Render mantigi
render()yordamchi metodiga koâchirilgan. - Element sahifaga kiritilganda uni bir marta chaqiramiz.
observedAttributes()da sanab oâtilgan atributning oâzgarishi uchunattributeChangedCallbackishga tushadi.- â¦va elementni qayta render qiladi.
- Oxirida biz osongina jonli taymer yasashimiz mumkin.
Render qilish tartibi
HTML parser DOM qurganda, elementlar ketma-ket qayta ishlanadi, ota-onalar bolalardan oldin. Masalan, agar bizda <outer><inner></inner></outer> boâlsa, <outer> elementi avval yaratiladi va DOM ga ulanadi, keyin <inner>.
Bu custom elementlar uchun muhim oqibatlarga olib keladi.
Masalan, agar custom element connectedCallback da innerHTML ga kirishga harakat qilsa, u hech narsa olmaydi:
<script>
customElements.define('user-info', class extends HTMLElement {
connectedCallback() {
alert(this.innerHTML); // bo'sh (*)
}
});
</script>
<user-info>John</user-info>
Agar uni ishga tushirsangiz, alert boâsh.
Bu aynan shu bosqichda bolalar yoâqligi sababli, DOM tugallanmagan. HTML parser custom element <user-info> ni uladi va uning bolalariga oâtishga tayyor, lekin hali yoâq.
Agar biz custom elementga maâlumot uzatmoqchi boâlsak, atributlardan foydalanishimiz mumkin. Ular darhol mavjud.
Yoki, agar bizga haqiqatan ham bolalar kerak boâlsa, nol kechikishli setTimeout bilan ularga kirishni kechiktirishimiz mumkin.
Bu ishlaydi:
<script>
customElements.define('user-info', class extends HTMLElement {
connectedCallback() {
setTimeout(() => alert(this.innerHTML)); // John (*)
}
});
</script>
<user-info>John</user-info>
Endi (*) satridagi alert âJohnâ ni koârsatadi, chunki biz uni HTML tahlili tugagandan keyin asinxron ravishda ishga tushiramiz. Agar kerak boâlsa, bolalarni qayta ishlashimiz va initsializatsiyani tugatishimiz mumkin.
Boshqa tomondan, bu yechim ham mukammal emas. Agar ichki custom elementlar ham oâzlarini initsializatsiya qilish uchun setTimeout dan foydalansa, ular navbatga turadilar: tashqi setTimeout avval, keyin ichki ishga tushadi.
Shunday qilib, tashqi element ichki elementdan oldin initsializatsiyani tugatadi.
Keling, buni misolda koârsataylik:
<script>
customElements.define('user-info', class extends HTMLElement {
connectedCallback() {
alert(`${this.id} connected.`);
setTimeout(() => alert(`${this.id} initialized.`));
}
});
</script>
<user-info id="outer">
<user-info id="inner"></user-info>
</user-info>
Chiqish tartibi:
- outer connected.
- inner connected.
- outer initialized.
- inner initialized.
Tashqi element ichki elementdan (4) oldin (3) initsializatsiyani tugatishini aniq koârishimiz mumkin.
Ichki elementlar tayyor boâlgandan keyin ishga tushadigan oârnatilgan callback yoâq. Agar kerak boâlsa, buni oâzimiz amalga oshirishimiz mumkin. Masalan, ichki elementlar initialized kabi hodisalarni joânatishi mumkin va tashqi elementlar ularni tinglashi va ularga javob berishi mumkin.
Moslashtirilgan oârnatilgan elementlar
Biz yaratgan yangi elementlar, masalan <time-formatted>, hech qanday bogâliq semantikaga ega emas. Ular qidiruv tizimlariga nomaâlum va accessibility qurilmalari ularni boshqara olmaydi.
Lekin bunday narsalar muhim boâlishi mumkin. Masalan, qidiruv tizimi biz haqiqatan ham vaqt koârsatayotganimizni bilishdan manfaatdor boâladi. Va agar biz maxsus turdagi tugma yasayotgan boâlsak, nima uchun mavjud <button> funksionalligini qayta ishlatmaymiz?
Biz ularning klasslaridan meros olib oârnatilgan HTML elementlarni kengaytirishimiz va moslashtira olamiz.
Masalan, tugmalar HTMLButtonElement nusxalari, keling, uni asosga olaylik.
-
Klassimiz bilan
HTMLButtonElementni kengaytiring:class HelloButton extends HTMLButtonElement { /* custom element metodlari */ } -
Tegni belgilaydigan
customElements.definega uchinchi argumentni taqdim eting:customElements.define('hello-button', HelloButton, {extends: 'button'});Bir xil DOM-klassni baham koâradigan turli teglar boâlishi mumkin, shuning uchun
extendsni belgilash kerak. -
Oxirida, custom elementimizdan foydalanish uchun oddiy
<button>tegini kiriting, lekin ungais="hello-button"qoâshing:<button is="hello-button">...</button>
Mana toâliq misol:
<script>
// Bosilganda "hello" deb aytadigan tugma
class HelloButton extends HTMLButtonElement {
constructor() {
super();
this.addEventListener('click', () => alert("Hello!"));
}
}
customElements.define('hello-button', HelloButton, {extends: 'button'});
</script>
<button is="hello-button">Meni bosing</button>
<button is="hello-button" disabled>O'chirilgan</button>
Bizning yangi tugmamiz oârnatilgan tugmani kengaytiradi. Shunday qilib, u bir xil uslublar va disabled atributi kabi standart xususiyatlarni saqlaydi.
Manbalar
- HTML Living Standard: https://html.spec.whatwg.org/#custom-elements.
- Muvofiqlik: https://caniuse.com/#feat=custom-elementsv1.
Xulosa
Custom elementlar ikki turdagi boâlishi mumkin:
-
âAvtonomâ â
HTMLElementni kengaytiradigan yangi teglar.Taârif sxemasi:
class MyElement extends HTMLElement { constructor() { super(); /* ... */ } connectedCallback() { /* ... */ } disconnectedCallback() { /* ... */ } static get observedAttributes() { return [/* ... */]; } attributeChangedCallback(name, oldValue, newValue) { /* ... */ } adoptedCallback() { /* ... */ } } customElements.define('my-element', MyElement); /* <my-element> */ -
âMoslashtirilgan oârnatilgan elementlarâ â mavjud elementlarning kengaytmalari.
Yana bitta
.defineargumenti va HTML dais="..."talab qiladi:class MyButton extends HTMLButtonElement { /*...*/ } customElements.define('my-button', MyElement, {extends: 'button'}); /* <button is="my-button"> */
Custom elementlar brauzerlar orasida yaxshi qoâllab-quvvatlanadi. Polyfill mavjud https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepenâ¦)