ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ ÑоздаваÑÑ Ð¿Ð¾Ð»ÑзоваÑелÑÑкие HTML-ÑлеменÑÑ, опиÑÑваемÑе наÑим клаÑÑом, Ñо Ñвоими меÑодами и ÑвойÑÑвами, ÑобÑÑиÑми и Ñак далее.
Ðак ÑолÑко полÑзоваÑелÑÑкий ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¾Ð¿ÑеделÑн, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ ÐµÐ³Ð¾ наÑавне Ñо вÑÑÑоеннÑми HTML-ÑлеменÑами.
ÐÑо замеÑаÑелÑно, Ð²ÐµÐ´Ñ ÑловаÑÑ HTML-Ñегов богаÑ, но не беÑконеÑен. Ðе ÑÑÑеÑÑвÑÐµÑ <easy-tabs>, <sliding-carousel>, <beautiful-upload>⦠ÐÑоÑÑо подÑмайÑе о лÑбом дÑÑгом Ñеге, коÑоÑÑй мог Ð±Ñ Ð½Ð°Ð¼ понадобиÑÑÑÑ.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ опÑеделиÑÑ Ð¸Ñ Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ ÑпеÑиалÑного клаÑÑа, а заÑем иÑполÑзоваÑÑ, как еÑли Ð±Ñ Ð¾Ð½Ð¸ вÑегда бÑли ÑаÑÑÑÑ HTML.
СÑÑеÑÑвÑÐµÑ Ð´Ð²Ð° вида полÑзоваÑелÑÑÐºÐ¸Ñ ÑлеменÑов:
- ÐвÑономнÑе полÑзоваÑелÑÑкие ÑлеменÑÑ â «полноÑÑÑÑ Ð½Ð¾Ð²Ñе» ÑлеменÑÑ, ÑаÑÑиÑÑÑÑие абÑÑÑакÑнÑй клаÑÑ
HTMLElement. - ÐолÑзоваÑелÑÑкие вÑÑÑоеннÑе ÑлеменÑÑ â ÑлеменÑÑ, ÑаÑÑиÑÑÑÑие вÑÑÑоеннÑе, напÑÐ¸Ð¼ÐµÑ ÐºÐ½Ð¾Ð¿ÐºÑ
HTMLButtonElementи Ñ.п.
СнаÑала Ð¼Ñ ÑазбеÑÑмÑÑ Ñ Ð°Ð²ÑономнÑми ÑлеменÑами, а заÑем пеÑейдÑм к полÑзоваÑелÑÑким вÑÑÑоеннÑм.
ЧÑÐ¾Ð±Ñ ÑоздаÑÑ Ð¿Ð¾Ð»ÑзоваÑелÑÑкий ÑлеменÑ, нам нÑжно ÑообÑиÑÑ Ð±ÑаÑзеÑÑ ÑÑд деÑалей о нÑм: как его показаÑÑ, ÑÑо делаÑÑ, когда ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÑеÑÑÑ Ð¸Ð»Ð¸ ÑдалÑеÑÑÑ Ñо ÑÑÑаниÑÑ Ð¸ Ñ.д.
ÐÑо делаеÑÑÑ Ð¿ÑÑÑм ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ»Ð°ÑÑа Ñо ÑпеÑиалÑнÑми меÑодами. ÐÑо пÑоÑÑо, Ñак как ÑÑÑеÑÑвÑÐµÑ Ð²Ñего неÑколÑко меÑодов, и вÑе они ÑвлÑÑÑÑÑ Ð½ÐµÐ¾Ð±ÑзаÑелÑнÑми.
ÐÐ¾Ñ Ð½Ð°Ð±ÑоÑок Ñ Ð¿Ð¾Ð»Ð½Ñм ÑпиÑком:
class MyElement extends HTMLElement {
constructor() {
super();
// ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ñоздан
}
connectedCallback() {
// бÑаÑÐ·ÐµÑ Ð²ÑзÑÐ²Ð°ÐµÑ ÑÑÐ¾Ñ Ð¼ÐµÑод пÑи добавлении ÑлеменÑа в докÑменÑ
    // (Ð¼Ð¾Ð¶ÐµÑ Ð²ÑзÑваÑÑÑÑ Ð¼Ð½Ð¾Ð³Ð¾ Ñаз, еÑли ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¼Ð½Ð¾Ð³Ð¾ÐºÑаÑно добавлÑеÑÑÑ/ÑдалÑеÑÑÑ)
}
disconnectedCallback() {
// бÑаÑÐ·ÐµÑ Ð²ÑзÑÐ²Ð°ÐµÑ ÑÑÐ¾Ñ Ð¼ÐµÑод пÑи Ñдалении ÑлеменÑа из докÑменÑа
// (Ð¼Ð¾Ð¶ÐµÑ Ð²ÑзÑваÑÑÑÑ Ð¼Ð½Ð¾Ð³Ð¾ Ñаз, еÑли ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¼Ð½Ð¾Ð³Ð¾ÐºÑаÑно добавлÑеÑÑÑ/ÑдалÑеÑÑÑ)
}
static get observedAttributes() {
return [/* маÑÑив имÑн аÑÑибÑÑов Ð´Ð»Ñ Ð¾ÑÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð¸Ñ
изменений */];
}
attributeChangedCallback(name, oldValue, newValue) {
// вÑзÑваеÑÑÑ Ð¿Ñи изменении одного из пеÑеÑиÑленнÑÑ
вÑÑе аÑÑибÑÑов
}
adoptedCallback() {
// вÑзÑваеÑÑÑ, когда ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¿ÐµÑемеÑаеÑÑÑ Ð² новÑй докÑменÑ
    // (пÑоиÑÑ
Ð¾Ð´Ð¸Ñ Ð² document.adoptNode, иÑполÑзÑеÑÑÑ Ð¾ÑÐµÐ½Ñ Ñедко)
}
// Ñ ÑлеменÑа могÑÑ Ð±ÑÑÑ ÐµÑÑ Ð´ÑÑгие меÑÐ¾Ð´Ñ Ð¸ ÑвойÑÑва
}
ÐоÑле ÑÑого нам нÑжно заÑегиÑÑÑиÑоваÑÑ ÑлеменÑ:
// ÑообÑим бÑаÑзеÑÑ, ÑÑо <my-element> обÑлÑживаеÑÑÑ Ð½Ð°Ñим новÑм клаÑÑом
customElements.define("my-element", MyElement);
ТепеÑÑ Ð´Ð»Ñ Ð»ÑбÑÑ
HTML-ÑлеменÑов Ñ Ñегом <my-element> ÑоздаÑÑÑÑ ÑкземплÑÑ MyElement и вÑзÑваÑÑÑÑ Ð²ÑÑеÑпомÑнÑÑÑе меÑодÑ. Также Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ document.createElement('my-element') в JavaScript.
-ÐÐ¼Ñ Ð¿Ð¾Ð»ÑзоваÑелÑÑкого ÑлеменÑа должно ÑодеÑжаÑÑ Ð´ÐµÑÐ¸Ñ -, напÑимеÑ, my-element и super-button â валиднÑе имена, а myelement â неÑ.
ÐÑо ÑÑÐ¾Ð±Ñ Ð³Ð°ÑанÑиÑоваÑÑ Ð¾ÑÑÑÑÑÑвие конÑликÑов имÑн Ð¼ÐµÐ¶Ð´Ñ Ð²ÑÑÑоеннÑми и полÑзоваÑелÑÑкими ÑлеменÑами HTML.
ÐÑимеÑ: «time-formatted»
ÐапÑимеÑ, ÑÐ»ÐµÐ¼ÐµÐ½Ñ <time> Ñже ÑÑÑеÑÑвÑÐµÑ Ð² HTML Ð´Ð»Ñ Ð´Ð°ÑÑ/вÑемени. Ðо Ñам по Ñебе он не вÑполнÑÐµÑ Ð½Ð¸ÐºÐ°ÐºÐ¾Ð³Ð¾ ÑоÑмаÑиÑованиÑ.
ÐавайÑе Ñоздадим ÑÐ»ÐµÐ¼ÐµÐ½Ñ <time-formatted>, коÑоÑÑй оÑобÑÐ°Ð¶Ð°ÐµÑ Ð²ÑÐµÐ¼Ñ Ð² Ñдобном ÑоÑмаÑе Ñ ÑÑÑÑом ÑзÑка:
<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>
- ÐлаÑÑ Ð¸Ð¼ÐµÐµÑ ÑолÑко один меÑод
connectedCallback()â бÑаÑÐ·ÐµÑ Ð²ÑзÑÐ²Ð°ÐµÑ ÐµÐ³Ð¾, когда ÑлеменÑ<time-formatted>добавлÑеÑÑÑ Ð½Ð° ÑÑÑаниÑÑ (или когда HTML-паÑÑÐµÑ Ð¾Ð±Ð½Ð°ÑÑÐ¶Ð¸Ð²Ð°ÐµÑ ÐµÐ³Ð¾), и он иÑполÑзÑÐµÑ Ð²ÑÑÑоеннÑй ÑоÑмаÑиÑовÑик даннÑÑ Intl.DateTimeFormat, Ñ Ð¾ÑоÑо поддеÑживаемÑй в бÑаÑзеÑÐ°Ñ , ÑÑÐ¾Ð±Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°ÑÑ ÐºÑаÑиво оÑÑоÑмаÑиÑованное вÑемÑ. - Ðам нÑжно заÑегиÑÑÑиÑоваÑÑ Ð½Ð°Ñ Ð½Ð¾Ð²Ñй ÑлеменÑ, иÑполÑзÑÑ
customElements.define(tag, class). - Ð Ñогда Ð¼Ñ Ñможем иÑполÑзоваÑÑ ÐµÐ³Ð¾ везде.
ÐÑли бÑаÑÐ·ÐµÑ ÑÑалкиваеÑÑÑ Ñ ÑлеменÑами <time-formatted> до customElements.define, Ñо ÑÑо не оÑибка. Ðо ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¿Ð¾ÐºÐ° неизвеÑÑен, как и лÑбой неÑÑандаÑÑнÑй Ñег.
Такие «неопÑеделÑннÑе» ÑлеменÑÑ Ð¼Ð¾Ð³ÑÑ Ð±ÑÑÑ ÑÑÐ¸Ð»Ð¸Ð·Ð¾Ð²Ð°Ð½Ñ Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ CSS ÑелекÑоÑа :not(:defined).
Ðогда вÑзÑваеÑÑÑ customElements.define, они «обновлÑÑÑÑÑ»: Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑоздаÑÑÑÑ Ð½Ð¾Ð²Ñй ÑкземплÑÑ TimeFormatted и вÑзÑваеÑÑÑ connectedCallback. Ðни ÑÑановÑÑÑÑ :defined.
ЧÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑиÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾ полÑзоваÑелÑÑÐºÐ¸Ñ ÑлеменÑÐ°Ñ , еÑÑÑ ÑледÑÑÑие меÑодÑ:
customElements.get(name)â возвÑаÑÐ°ÐµÑ ÐºÐ»Ð°ÑÑ Ð¿Ð¾Ð»ÑзоваÑелÑÑкого ÑлеменÑа Ñ ÑказаннÑм именемname,customElements.whenDefined(name)â возвÑаÑÐ°ÐµÑ Ð¿ÑомиÑ, коÑоÑÑй пеÑÐµÑ Ð¾Ð´Ð¸Ñ Ð² ÑоÑÑоÑние «ÑÑпеÑно вÑполнен» Ñо знаÑением конÑÑÑÑкÑоÑа полÑзоваÑелÑÑкого ÑлеменÑа, когда опÑеделÑн полÑзоваÑелÑÑкий ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ñ ÑказаннÑм именемname.
connectedCallback, не в constructorРпÑиведÑнном вÑÑе пÑимеÑе ÑодеÑжимое ÑлеменÑа ÑендеÑиÑÑÑ (ÑоздаÑÑÑÑ) в connectedCallback.
ÐоÑÐµÐ¼Ñ Ð½Ðµ в constructor?
ÐÑиÑина пÑоÑÑа: когда вÑзÑваеÑÑÑ constructor, делаÑÑ ÑÑо ÑлиÑком Ñано. ÐкземплÑÑ ÑлеменÑа Ñоздан, но на ÑÑом ÑÑапе бÑаÑÐ·ÐµÑ ÐµÑÑ Ð½Ðµ обÑабоÑал/назнаÑил аÑÑибÑÑÑ: вÑÐ·Ð¾Ð²Ñ getAttribute веÑнÑли Ð±Ñ null. Так ÑÑо Ð¼Ñ Ð½Ðµ можем ÑендеÑиÑÑ Ð·Ð´ÐµÑÑ.
ÐÑоме Ñого, еÑли подÑмаÑÑ, ÑÑо лÑÑÑе Ñ ÑоÑки зÑÐµÐ½Ð¸Ñ Ð¿ÑоизводиÑелÑноÑÑи â оÑложиÑÑ ÑабоÑÑ Ð´Ð¾ ÑÐµÑ Ð¿Ð¾Ñ, пока она дейÑÑвиÑелÑно не понадобиÑÑÑ.
connectedCallback ÑÑабаÑÑваеÑ, когда ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÑеÑÑÑ Ð² докÑменÑ. Ðе пÑоÑÑо добавлÑеÑÑÑ Ðº дÑÑÐ³Ð¾Ð¼Ñ ÑлеменÑÑ ÐºÐ°Ðº доÑеÑний, но ÑакÑиÑеÑки ÑÑановиÑÑÑ ÑаÑÑÑÑ ÑÑÑаниÑÑ. Таким обÑазом, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ поÑÑÑоиÑÑ Ð¾ÑделÑнÑй DOM, ÑоздаÑÑ ÑлеменÑÑ Ð¸ подгоÑовиÑÑ Ð¸Ñ
Ð´Ð»Ñ Ð¿Ð¾ÑледÑÑÑего иÑполÑзованиÑ. Ðни бÑдÑÑ ÑендеÑиÑÑÑÑ ÑолÑко Ñогда, когда попадÑÑ Ð½Ð° ÑÑÑаниÑÑ.
ÐаблÑдение за аÑÑибÑÑами
Ð ÑекÑÑей ÑеализаÑии <time-formatted> поÑле Ñого, как ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¾ÑÑендеÑилÑÑ, далÑнейÑие Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð°ÑÑибÑÑов не даÑÑ Ð½Ð¸ÐºÐ°ÐºÐ¾Ð³Ð¾ ÑÑÑекÑа. ÐÑо ÑÑÑанно Ð´Ð»Ñ HTML-ÑлеменÑа. ÐбÑÑно, когда Ð¼Ñ Ð¸Ð·Ð¼ÐµÐ½Ñем аÑÑибÑÑ, напÑÐ¸Ð¼ÐµÑ a.href, Ð¼Ñ Ð¾Ð¶Ð¸Ð´Ð°ÐµÐ¼, ÑÑо изменение бÑÐ´ÐµÑ Ð²Ð¸Ð´Ð½Ð¾ ÑÑазÑ. Так ÑÑо давайÑе иÑпÑавим ÑÑо.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ наблÑдаÑÑ Ð·Ð° аÑÑибÑÑами, помеÑÑив иÑ
ÑпиÑок в ÑÑаÑиÑеÑкий геÑÑÐµÑ observedAttributes(). ÐÑи изменении ÑакиÑ
аÑÑибÑÑов вÑзÑваеÑÑÑ attributeChangedCallback. Ðн ÑÑабаÑÑÐ²Ð°ÐµÑ Ð½Ðµ Ð´Ð»Ñ Ð»Ñбого аÑÑибÑÑа по ÑообÑажениÑм пÑоизводиÑелÑноÑÑи.
ÐÐ¾Ñ Ð½Ð¾Ð²Ñй <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(). - ÐÑ Ð²ÑзÑваем его один Ñаз, когда ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð²ÑÑавлÑеÑÑÑ Ð½Ð° ÑÑÑаниÑÑ.
- ÐÑи изменении аÑÑибÑÑа, Ñказанного в
observedAttributes(), вÑзÑваеÑÑÑattributeChangedCallback. - â¦Ð¸ пÑоиÑÑ Ð¾Ð´Ð¸Ñ ÑеÑендеÑинг ÑлеменÑа.
- РконÑе Ð¼Ñ Ð»ÐµÐ³ÐºÐ¾ ÑоздаÑм живой ÑаймеÑ.
ÐоÑÑдок ÑендеÑинга
Ðогда HTML-паÑÑÐµÑ ÑÑÑÐ¾Ð¸Ñ DOM, ÑлеменÑÑ Ð¾Ð±ÑабаÑÑваÑÑÑÑ Ð´ÑÑг за дÑÑгом, ÑодиÑели до деÑей. ÐапÑимеÑ, еÑли Ñ Ð½Ð°Ñ ÐµÑÑÑ <outer><inner></inner></outer>, Ñо ÑÐ»ÐµÐ¼ÐµÐ½Ñ <outer> ÑоздаÑÑÑÑ Ð¸ вклÑÑаеÑÑÑ Ð² DOM пеÑвÑм, а заÑем <inner>.
ÐÑо пÑÐ¸Ð²Ð¾Ð´Ð¸Ñ Ðº важнÑм поÑледÑÑвиÑм Ð´Ð»Ñ Ð¿Ð¾Ð»ÑзоваÑелÑÑÐºÐ¸Ñ ÑлеменÑов.
ÐапÑимеÑ, еÑли полÑзоваÑелÑÑкий ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¿ÑÑаеÑÑÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к innerHTML в connectedCallback, он ниÑего не полÑÑаеÑ:
<script>
customElements.define('user-info', class extends HTMLElement {
connectedCallback() {
alert(this.innerHTML); // пÑÑÑо (*)
}
});
</script>
<user-info>Ðжон</user-info>
ÐÑли Ð²Ñ Ð·Ð°Ð¿ÑÑÑиÑе ÑÑо, alert бÑÐ´ÐµÑ Ð¿ÑÑÑ.
ÐÑо пÑоиÑÑ
Ð¾Ð´Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð½Ð¾ поÑомÑ, ÑÑо на ÑÑой ÑÑадии еÑÑ Ð½Ðµ ÑÑÑеÑÑвÑÑÑ Ð´Ð¾ÑеÑние ÑлеменÑÑ, DOM не завеÑÑÑн. HTML-паÑÑÐµÑ Ð¿Ð¾Ð´ÐºÐ»ÑÑил полÑзоваÑелÑÑкий ÑÐ»ÐµÐ¼ÐµÐ½Ñ <user-info> и ÑепеÑÑ ÑобиÑаеÑÑÑ Ð¿ÐµÑейÑи к его доÑеÑним ÑлеменÑам, но пока не Ñделал ÑÑого.
ÐÑли Ð¼Ñ Ñ Ð¾Ñим пеÑедаÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð² полÑзоваÑелÑÑкий ÑлеменÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ Ð°ÑÑибÑÑÑ. Ðни доÑÑÑÐ¿Ð½Ñ ÑÑазÑ.
Ðли, еÑли нам дейÑÑвиÑелÑно нÑÐ¶Ð½Ñ Ð´Ð¾ÑеÑние ÑлеменÑÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ оÑложиÑÑ Ð´Ð¾ÑÑÑп к ним, иÑполÑзÑÑ setTimeout Ñ Ð½Ñлевой задеÑжкой.
ÐÑо ÑабоÑаеÑ:
<script>
customElements.define('user-info', class extends HTMLElement {
connectedCallback() {
setTimeout(() => alert(this.innerHTML)); // Ðжон (*)
}
});
</script>
<user-info>Ðжон</user-info>
ТепеÑÑ alert в ÑÑÑоке (*) показÑÐ²Ð°ÐµÑ Â«Ðжон», поÑколÑÐºÑ Ð¼Ñ Ð·Ð°Ð¿ÑÑкаем его аÑинÑ
Ñонно, поÑле завеÑÑÐµÐ½Ð¸Ñ Ð¿Ð°ÑÑинга HTML. ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ обÑабоÑаÑÑ Ð´Ð¾ÑеÑние ÑлеменÑÑ Ð¿Ñи необÑ
одимоÑÑи и завеÑÑиÑÑ Ð¸Ð½Ð¸ÑиализаÑиÑ.
С дÑÑгой ÑÑоÑонÑ, ÑÑо ÑеÑение Ñакже не идеалÑно. ÐÑли вложеннÑе полÑзоваÑелÑÑкие ÑлеменÑÑ Ñоже иÑполÑзÑÑÑ setTimeout Ð´Ð»Ñ Ð¸Ð½Ð¸ÑиализаÑии, Ñо они вÑÑаÑÑ Ð² оÑеÑедÑ: пеÑвÑм запÑÑкаеÑÑÑ Ð²Ð½ÐµÑний setTimeout, а заÑем внÑÑÑенний.
Так ÑÑо внеÑний ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð·Ð°Ð²ÐµÑÑÐ°ÐµÑ Ð¸Ð½Ð¸ÑиализаÑÐ¸Ñ ÑанÑÑе внÑÑÑеннего.
ÐÑодемонÑÑÑиÑÑем ÑÑо на пÑимеÑе:
<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>
ÐоÑÑдок вÑвода:
- outer connected.
- inner connected.
- outer initialized.
- inner initialized.
ÐÑ ÑÑно видим, ÑÑо внеÑний ÑÐ»ÐµÐ¼ÐµÐ½Ñ outer завеÑÑÐ°ÐµÑ Ð¸Ð½Ð¸ÑиализаÑÐ¸Ñ (3) до внÑÑÑеннего inner (4).
ÐÐµÑ Ð²ÑÑÑоенного колбÑка, коÑоÑÑй ÑÑабаÑÑÐ²Ð°ÐµÑ Ð¿Ð¾Ñле Ñого, как вложеннÑе ÑлеменÑÑ Ð³Ð¾ÑовÑ. ÐÑли нÑжно, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑеализоваÑÑ Ð¿Ð¾Ð´Ð¾Ð±Ð½Ð¾Ðµ ÑамоÑÑоÑÑелÑно. ÐапÑимеÑ, внÑÑÑенние ÑлеменÑÑ Ð¼Ð¾Ð³ÑÑ Ð¾ÑпÑавлÑÑÑ ÑобÑÑÐ¸Ñ Ð½Ð°Ð¿Ð¾Ð´Ð¾Ð±Ð¸Ðµ initialized, а внеÑние могÑÑ ÑлÑÑаÑÑ Ð¸ ÑеагиÑоваÑÑ Ð½Ð° ниÑ
.
ÐодиÑиÑиÑованнÑе вÑÑÑоеннÑе ÑлеменÑÑ
ÐовÑе ÑлеменÑÑ, коÑоÑÑе Ð¼Ñ ÑоздаÑм, Ñакие как <time-formatted>, не имеÑÑ ÑвÑзанной Ñ Ð½Ð¸Ð¼Ð¸ ÑеманÑики. Ðни не извеÑÑÐ½Ñ Ð¿Ð¾Ð¸ÑковÑм ÑиÑÑемам, а ÑÑÑÑойÑÑва Ð´Ð»Ñ Ð»Ñдей Ñ Ð¾Ð³ÑаниÑеннÑми возможноÑÑÑми не могÑÑ ÑпÑавиÑÑÑÑ Ñ Ð½Ð¸Ð¼Ð¸.
Ðо Ñакие веÑи могÑÑ Ð±ÑÑÑ Ð²Ð°Ð¶Ð½Ñ. ÐапÑимеÑ, поиÑковой ÑиÑÑеме бÑло Ð±Ñ Ð¸Ð½ÑеÑеÑно ÑзнаÑÑ, ÑÑо Ð¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ñваем именно вÑемÑ. РеÑли Ð¼Ñ Ð´ÐµÐ»Ð°ÐµÐ¼ ÑпеÑиалÑнÑй вид кнопки, поÑÐµÐ¼Ñ Ð½Ðµ иÑполÑзоваÑÑ ÑÑÑеÑÑвÑÑÑÑÑ ÑÑнкÑионалÑноÑÑÑ <button>?
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ ÑаÑÑиÑÑÑÑ Ð¸ модиÑиÑиÑоваÑÑ Ð²ÑÑÑоеннÑе HTML-ÑлеменÑÑ, наÑледÑÑ Ð¸Ñ ÐºÐ»Ð°ÑÑÑ.
ÐапÑимеÑ, кнопки <button> ÑвлÑÑÑÑÑ ÑкземплÑÑами клаÑÑа HTMLButtonElement, давайÑе поÑÑÑоим ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð½Ð° его оÑнове.
-
УнаÑледÑем
HTMLButtonElementнаÑим клаÑÑом:class HelloButton extends HTMLButtonElement { /* меÑÐ¾Ð´Ñ Ð¿Ð¾Ð»ÑзоваÑелÑÑкого ÑлеменÑа */ } -
ÐÑедоÑÑавим ÑÑеÑий аÑгÑÐ¼ÐµÐ½Ñ Ð²
customElements.define, ÑказÑваÑÑий Ñег:customElements.define('hello-button', HelloButton, {extends: 'button'});ÐÑваеÑ, ÑÑо ÑазнÑе Ñеги имеÑÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñй DOM-клаÑÑ, поÑÑÐ¾Ð¼Ñ Ñказание Ñега Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾.
-
РконÑе, ÑÑÐ¾Ð±Ñ Ð¸ÑполÑзоваÑÑ Ð½Ð°Ñ Ð¿Ð¾Ð»ÑзоваÑелÑÑкий ÑлеменÑ, вÑÑавим обÑÑнÑй Ñег
<button>, но добавим к немÑis="hello-button":<button is="hello-button">...</button>
ÐÐ¾Ñ Ð¿Ð¾Ð»Ð½Ñй пÑимеÑ:
<script>
// Ðнопка, говоÑÑÑÐ°Ñ "пÑивеÑ" по кликÑ
class HelloButton extends HTMLButtonElement {
constructor() {
super();
this.addEventListener('click', () => alert("ÐÑивеÑ!"));
}
}
customElements.define('hello-button', HelloButton, {extends: 'button'});
</script>
<button is="hello-button">Ðажми на менÑ</button>
<button is="hello-button" disabled>ÐÑклÑÑена</button>
ÐаÑа Ð½Ð¾Ð²Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° ÑаÑÑиÑÑÐµÑ Ð²ÑÑÑоеннÑÑ. Так ÑÑо она ÑоÑ
ÑанÑÐµÑ Ñе же ÑÑили и ÑÑандаÑÑнÑе возможноÑÑи, наподобие аÑÑибÑÑа disabled.
СÑÑлки
- HTML Living Standard: https://html.spec.whatwg.org/#custom-elements.
- СовмеÑÑимоÑÑÑ: https://caniuse.com/#feat=custom-elementsv1.
ÐÑого
ÐÑÑÑ Ð´Ð²Ð° Ñипа полÑзоваÑелÑÑÐºÐ¸Ñ ÑлеменÑов:
-
«ÐвÑономнÑе» â новÑе Ñеги, ÑаÑÑиÑÑÑÑие
HTMLElement.Ð¡Ñ ÐµÐ¼Ð° опÑеделениÑ:
class MyElement extends HTMLElement { constructor() { super(); /* ... */ } connectedCallback() { /* ... */ } disconnectedCallback() { /* ... */ } static get observedAttributes() { return [/* ... */]; } attributeChangedCallback(name, oldValue, newValue) { /* ... */ } adoptedCallback() { /* ... */ } } customElements.define('my-element', MyElement); /* <my-element> */ -
«ÐодиÑиÑиÑованнÑе вÑÑÑоеннÑе ÑлеменÑÑ» â ÑаÑÑиÑÐµÐ½Ð¸Ñ ÑÑÑеÑÑвÑÑÑÐ¸Ñ ÑлеменÑов.
ТÑебÑÑÑ ÐµÑÑ Ð¾Ð´Ð¸Ð½ аÑгÑÐ¼ÐµÐ½Ñ Ð²
.defineи аÑÑибÑÑis="..."в HTML:class MyButton extends HTMLButtonElement { /*...*/ } customElements.define('my-button', MyElement, {extends: 'button'}); /* <button is="my-button"> */
ÐолÑзоваÑелÑÑкие ÑлеменÑÑ ÑиÑоко поддеÑживаÑÑÑÑ ÑÑеди бÑаÑзеÑов. СÑÑеÑÑвÑÐµÑ Ð¿Ð¾Ð»Ð¸Ñил: https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)