Ðногим Ñипам компоненÑов, Ñаким как вкладки, менÑ, галеÑеи изобÑажений и дÑÑгие, нÑжно какое-Ñо ÑодеÑжимое Ð´Ð»Ñ Ð¾ÑобÑажениÑ.
Так же, как вÑÑÑоеннÑй в бÑаÑÐ·ÐµÑ <select> Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ ÐºÐ¾Ð½ÑÐµÐ½Ñ Ð¿ÑнкÑов <option>, ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ <custom-tabs> Ð¼Ð¾Ð¶ÐµÑ Ð¾Ð¶Ð¸Ð´Ð°ÑÑ, ÑÑо бÑÐ´ÐµÑ Ð¿ÐµÑедано ÑакÑиÑеÑкое ÑодеÑжимое вкладок, а <custom-menu> â пÑнкÑов менÑ.
Ðод, иÑполÑзÑÑÑий Ð¼ÐµÐ½Ñ <custom-menu>, Ð¼Ð¾Ð¶ÐµÑ Ð²ÑглÑдеÑÑ Ñак:
<custom-menu>
<title>СладоÑÑи</title>
<item>ÐеденÑÑ</item>
<item>ФÑÑкÑовÑе ÑоÑÑÑ</item>
<item>ÐекÑÑ</item>
</custom-menu>
â¦ÐаÑем ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ пÑавилÑно его оÑобÑазиÑÑ â как обÑÑное Ð¼ÐµÐ½Ñ Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñм названием и пÑнкÑами, обÑабаÑÑваÑÑ ÑобÑÑÐ¸Ñ Ð¼ÐµÐ½Ñ Ð¸ Ñ.д.
Ðак ÑÑо ÑеализоваÑÑ?
Ðожно попÑобоваÑÑ Ð¿ÑоанализиÑоваÑÑ ÑодеÑжимое ÑлеменÑа и динамиÑеÑки ÑкопиÑоваÑÑ Ð¸ пеÑеÑÑавиÑÑ DOM-ÑзлÑ. ÐÑо возможно, но еÑли Ð¼Ñ Ð±Ñдем пеÑемеÑаÑÑ ÑлеменÑÑ Ð² Ñеневой DOM, CSS-ÑÑили докÑменÑа не бÑдÑÑ Ð¿ÑименÑÑÑÑÑ, и Ð¼Ñ Ð¿Ð¾ÑеÑÑем визÑалÑное оÑоÑмление. ÐÑоме Ñого, нÑжно бÑÐ´ÐµÑ Ð¿Ð¸ÑаÑÑ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸ÑелÑнÑй код.
Ð ÑÑаÑÑÑÑ, нам ÑÑого делаÑÑ Ð½Ðµ нÑжно. Теневой DOM поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ ÑлеменÑÑ <slot>, коÑоÑÑе авÑомаÑиÑеÑки наполнÑÑÑÑÑ ÐºÐ¾Ð½ÑенÑом из обÑÑного, «ÑвеÑлого» DOM-деÑева.
ÐменованнÑе ÑлоÑÑ
ÐавайÑе ÑаÑÑмоÑÑим ÑабоÑÑ ÑлоÑов на пÑоÑÑом пÑимеÑе.
Теневой DOM <user-card> Ð¸Ð¼ÐµÐµÑ Ð´Ð²Ð° ÑлоÑа, заполнÑемÑÑ
из обÑÑного DOM:
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<div>ÐмÑ:
<slot name="username"></slot>
</div>
<div>ÐаÑа ÑождениÑ:
<slot name="birthday"></slot>
</div>
`;
}
});
</script>
<user-card>
<span slot="username">Ðван Ðванов</span>
<span slot="birthday">01.01.2001</span>
</user-card>
Ð Ñеневом DOM <slot name="X"> опÑеделÑÐµÑ Â«ÑоÑÐºÑ Ð²ÑÑавки» â меÑÑо, где оÑобÑажаÑÑÑÑ ÑлеменÑÑ Ñ slot="X".
ÐаÑем бÑаÑÐ·ÐµÑ Ð²ÑполнÑÐµÑ Â«ÐºÐ¾Ð¼Ð¿Ð¾Ð·Ð¸ÑиÑ»: беÑÑÑ ÑлеменÑÑ Ð¸Ð· обÑÑного DOM-деÑева и оÑобÑÐ°Ð¶Ð°ÐµÑ Ð¸Ñ Ð² ÑооÑвеÑÑÑвÑÑÑÐ¸Ñ ÑлоÑÐ°Ñ Ñеневого DOM-деÑева. Ð ÑезÑлÑÑаÑе Ð¼Ñ Ð¿Ð¾Ð»ÑÑаем именно Ñо, ÑÑо Ñ Ð¾Ñели â компоненÑ, коÑоÑÑй можно наполниÑÑ Ð´Ð°Ð½Ð½Ñми.
ÐоÑле вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑкÑипÑа ÑÑÑÑкÑÑÑа DOM вÑглÑÐ´Ð¸Ñ ÑледÑÑÑим обÑазом (без ÑÑÑÑа композиÑии):
<user-card>
#shadow-root
<div>ÐмÑ:
<slot name="username"></slot>
</div>
<div>ÐаÑа ÑождениÑ:
<slot name="birthday"></slot>
</div>
<span slot="username">Ðван Ðванов</span>
<span slot="birthday">01.01.2001</span>
</user-card>
ÐÑ Ñоздали Ñеневой DOM, он изобÑажÑн под #shadow-root. ТепеÑÑ Ñ ÑлеменÑа еÑÑÑ Ð´Ð²Ð° DOM-деÑева: обÑÑное («ÑвеÑлое») и Ñеневое.
ЧÑÐ¾Ð±Ñ Ð¾ÑобÑазиÑÑ ÑодеÑжимое, Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ <slot name="..."> в Ñеневом DOM бÑаÑÐ·ÐµÑ Ð¸ÑÐµÑ slot="..." Ñ Ñаким же именем в обÑÑном DOM. ÐÑи ÑлеменÑÑ Ð¾ÑобÑажаÑÑÑÑ Ð²Ð½ÑÑÑи ÑлоÑов:
Ð ÑезÑлÑÑаÑе вÑÑÑÑаиваеÑÑÑ Ñак назÑваемое «ÑазвÑÑнÑÑое» (flattened) DOM-деÑево:
<user-card>
#shadow-root
<div>ÐмÑ:
<slot name="username">
<!-- ÑÐ»ÐµÐ¼ÐµÐ½Ñ ÑлоÑа вÑÑавлÑеÑÑÑ Ð² ÑÐ»Ð¾Ñ -->
<span slot="username">Ðван Ðванов</span>
</slot>
</div>
<div>ÐаÑа ÑождениÑ:
<slot name="birthday">
<span slot="birthday">01.01.2001</span>
</slot>
</div>
</user-card>
â¦Ðо ÑазвÑÑнÑÑое DOM-деÑево ÑÑÑеÑÑвÑÐµÑ ÑолÑко Ð´Ð»Ñ Ñелей оÑобÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð¸ обÑабоÑки ÑобÑÑий. ÐÑо Ñо, ÑÑо Ð¼Ñ Ð²Ð¸Ð´Ð¸Ð¼ на ÑкÑане. Ðно, в некоÑоÑом плане, «виÑÑÑалÑное». ФакÑиÑеÑки в докÑменÑе ÑаÑположение Ñзлов не менÑеÑÑÑ.
ÐÑо можно легко пÑовеÑиÑÑ, запÑÑÑив querySelectorAll: вÑе ÑÐ·Ð»Ñ Ð½Ð°Ñ
одÑÑÑÑ Ð½Ð° ÑвоиÑ
меÑÑаÑ
.
// ÑÐ·Ð»Ñ ÑвеÑлого DOM наÑ
одÑÑÑÑ Ð² Ñом же меÑÑе, в `<user-card>`
alert( document.querySelectorAll('user-card span').length ); // 2
Так ÑÑо ÑазвÑÑнÑÑÑй DOM ÑоÑÑавлÑеÑÑÑ Ð¸Ð· Ñеневого вÑÑавкой в ÑлоÑÑ. ÐÑаÑÐ·ÐµÑ Ð¸ÑполÑзÑÐµÑ ÐµÐ³Ð¾ Ð´Ð»Ñ ÑендеÑинга и пÑи вÑплÑÑии ÑобÑÑий (об ÑÑом позже). Ðо JavaScript Ð²Ð¸Ð´Ð¸Ñ Ð´Ð¾ÐºÑÐ¼ÐµÐ½Ñ Â«ÐºÐ°Ðº еÑÑÑ» â до поÑÑÑÐ¾ÐµÐ½Ð¸Ñ ÑазвÑÑнÑÑого DOM-деÑева.
ÐÑÑибÑÑ slot="..." ÑабоÑÐ°ÐµÑ ÑолÑко на непоÑÑедÑÑвеннÑÑ
деÑÑÑ
ÑлеменÑа-Ñ
озÑина Ñеневого деÑева (в наÑем пÑимеÑе ÑÑо ÑÐ»ÐµÐ¼ÐµÐ½Ñ <user-card>). ÐÐ»Ñ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½ÑÑ
ÑлеменÑов он игноÑиÑÑеÑÑÑ.
ÐапÑимеÑ, здеÑÑ Ð²ÑоÑой <span> игноÑиÑÑеÑÑÑ (Ñак как он не ÑвлÑеÑÑÑ Ð¿Ð¾Ñомком веÑÑ
него ÑÑÐ¾Ð²Ð½Ñ ÑлеменÑа <user-card>):
<user-card>
<span slot="username">Ðван Ðванов</span>
<div>
<!-- некоÑÑекÑнÑй ÑлоÑ, должен бÑÑÑ Ð½Ð° веÑÑ
нем ÑÑовне user-card: -->
<span slot="birthday">01.01.2001</span>
</div>
</user-card>
ÐÑли в ÑвеÑлом DOM еÑÑÑ Ð½ÐµÑколÑко ÑлеменÑов Ñ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñм именем ÑлоÑа, они добавлÑÑÑÑÑ Ð² ÑÐ»Ð¾Ñ Ð¾Ð´Ð¸Ð½ за дÑÑгим.
ÐапÑимеÑ, ÑÑÐ¾Ñ ÐºÐ¾Ð´:
<user-card>
<span slot="username">Ðван</span>
<span slot="username">Ðванов</span>
</user-card>
ÐаÑÑ Ñакой ÑазвÑÑнÑÑÑй DOM Ñ Ð´Ð²ÑÐ¼Ñ ÑлеменÑами в <slot name="username">:
<user-card>
#shadow-root
<div>ÐмÑ:
<slot name="username">
<span slot="username">Ðван</span>
<span slot="username">Ðванов</span>
</slot>
</div>
<div>ÐаÑа ÑождениÑ:
<slot name="birthday"></slot>
</div>
</user-card>
СодеÑжимое ÑлоÑа «по ÑмолÑаниÑ»
ÐÑли Ð¼Ñ Ð´Ð¾Ð±Ð°Ð²Ð»Ñем даннÑе в <slot>, ÑÑо ÑÑановиÑÑÑ ÑодеÑжимÑм «по ÑмолÑаниÑ». ÐÑаÑÐ·ÐµÑ Ð¾ÑобÑÐ°Ð¶Ð°ÐµÑ ÐµÐ³Ð¾, еÑли в ÑвеÑлом DOM-деÑеве оÑÑÑÑÑÑвÑÑÑ Ð´Ð°Ð½Ð½Ñе Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑлоÑа.
ÐапÑимеÑ, в ÑÑой ÑаÑÑи Ñеневого деÑева ÑекÑÑ Ðноним оÑобÑажаеÑÑÑ, еÑли в ÑвеÑлом деÑеве Ð½ÐµÑ Ð·Ð½Ð°ÑÐµÐ½Ð¸Ñ slot="username".
<div>ÐмÑ:
<slot name="username">Ðноним</slot>
</div>
Ð¡Ð»Ð¾Ñ Ð¿Ð¾ ÑмолÑÐ°Ð½Ð¸Ñ (пеÑвÑй без имени)
ÐеÑвÑй <slot> в Ñеневом деÑеве без аÑÑибÑÑа name ÑвлÑеÑÑÑ ÑлоÑом по ÑмолÑаниÑ. Ðн бÑÐ´ÐµÑ Ð¾ÑобÑажаÑÑ Ð´Ð°Ð½Ð½Ñе Ñо вÑеÑ
Ñзлов ÑвеÑлого деÑева, не добавленнÑе в дÑÑгие ÑлоÑÑ
ÐапÑимеÑ, давайÑе добавим ÑÐ»Ð¾Ñ Ð¿Ð¾ ÑмолÑÐ°Ð½Ð¸Ñ Ð² Ð½Ð°Ñ ÑÐ»ÐµÐ¼ÐµÐ½Ñ <user-card>; он бÑÐ´ÐµÑ ÑобиÑаÑÑ Ð²ÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾ полÑзоваÑеле, не занеÑÑннÑÑ Ð² дÑÑгие ÑлоÑÑ:
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<div>ÐмÑ:
<slot name="username"></slot>
</div>
<div>ÐаÑа ÑождениÑ:
<slot name="birthday"></slot>
</div>
<fieldset>
<legend>ÐÑÑÐ³Ð°Ñ Ð¸Ð½ÑоÑмаÑиÑ</legend>
<slot></slot>
</fieldset>
`;
}
});
</script>
<user-card>
<div>Я лÑÐ±Ð»Ñ Ð¿Ð»Ð°Ð²Ð°ÑÑ.</div>
<span slot="username">Ðван Ðванов</span>
<span slot="birthday">01.01.2001</span>
<div>...РигÑаÑÑ Ð² волейбол!</div>
</user-card>
ÐÑÑ ÑодеÑжимое обÑÑного деÑева, не добавленное в ÑлоÑÑ, попало в <fieldset> «ÐÑÑÐ³Ð°Ñ Ð¸Ð½ÑоÑмаÑиÑ».
ÐлеменÑÑ Ð´Ð¾Ð±Ð°Ð²Ð»ÑÑÑÑÑ Ð² ÑÐ»Ð¾Ñ Ð¿Ð¾ оÑеÑеди, один за дÑÑгим, поÑÑÐ¾Ð¼Ñ Ð¾Ð±Ð° ÑлеменÑа даннÑÑ , коÑоÑÑе не бÑли Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ñ Ð² ÑлоÑÑ, попадаÑÑ Ð² ÑÐ»Ð¾Ñ Ð¿Ð¾ ÑмолÑаниÑ.
РазвÑÑнÑÑое DOM-деÑево вÑглÑÐ´Ð¸Ñ Ñак:
<user-card>
#shadow-root
<div>ÐмÑ:
<slot name="username">
<span slot="username">Ðван Ðванов</span>
</slot>
</div>
<div>ÐаÑа ÑождениÑ:
<slot name="birthday">
<span slot="birthday">01.01.2001</span>
</slot>
</div>
<fieldset>
<legend>ÐÑÑÐ³Ð°Ñ Ð¸Ð½ÑоÑмаÑиÑ</legend>
<slot>
<div>Я лÑÐ±Ð»Ñ Ð¿Ð»Ð°Ð²Ð°ÑÑ.</div>
<div>...РигÑаÑÑ Ð² волейбол!</div>
</slot>
</fieldset>
</user-card>
ÐÑÐ¸Ð¼ÐµÑ Ð¼ÐµÐ½Ñ
ÐавайÑе веÑнÑмÑÑ Ðº Ð¼ÐµÐ½Ñ <custom-menu>, ÑпомÑнÑÑÐ¾Ð¼Ñ Ð² наÑале главÑ.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ ÑлоÑÑ Ð´Ð»Ñ ÑаÑпÑÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ ÑлеменÑов.
ÐÐ¾Ñ ÑазмеÑка Ð´Ð»Ñ Ð¼ÐµÐ½Ñ <custom-menu>:
<custom-menu>
<span slot="title">СладоÑÑи</span>
<li slot="item">ÐеденÑÑ</li>
<li slot="item">ФÑÑкÑовÑе ÑоÑÑÑ</li>
<li slot="item">ÐекÑÑ</li>
</custom-menu>
Шаблон Ñеневого DOM-деÑева Ñ Ð¿ÑавилÑнÑми ÑлоÑами:
<template id="tmpl">
<style> /* ÑÑили Ð¼ÐµÐ½Ñ */ </style>
<div class="menu">
<slot name="title"></slot>
<ul><slot name="item"></slot></ul>
</div>
</template>
<span slot="title">Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ Ð²<slot name="title">.- Ð Ñаблоне много ÑлеменÑов
<li slot="item">, но ÑолÑко один ÑлоÑ<slot name="item">. ÐоÑÑÐ¾Ð¼Ñ Ð²Ñе Ñакие<li slot="item">добавлÑÑÑÑÑ Ð²<slot name="item">один за дÑÑгим, ÑоÑмиÑÑÑ ÑпиÑок.
РазвÑÑнÑÑое DOM-деÑево ÑÑановиÑÑÑ Ñаким:
<custom-menu>
#shadow-root
<style> /* ÑÑили Ð¼ÐµÐ½Ñ */ </style>
<div class="menu">
<slot name="title">
<span slot="title">СладоÑÑи</span>
</slot>
<ul>
<slot name="item">
<li slot="item">ÐеденÑÑ</li>
<li slot="item">ФÑÑкÑовÑе ÑоÑÑÑ</li>
<li slot="item">ÐекÑÑ</li>
</slot>
</ul>
</div>
</custom-menu>
Ðожно замеÑиÑÑ, ÑÑо в валидном DOM-деÑеве Ñег <li> должен бÑÑÑ Ð¿ÑÑмÑм поÑомком Ñега <ul>. Ðо ÑÑо ÑазвÑÑнÑÑÑй DOM, коÑоÑÑй опиÑÑÐ²Ð°ÐµÑ Ñо, как ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ Ð¾ÑобÑажаеÑÑÑ, в нÑм ÑÐ°ÐºÐ°Ñ ÑиÑÑаÑÐ¸Ñ Ð½Ð¾ÑмалÑна.
ÐÑÑалоÑÑ ÑолÑко добавиÑÑ Ð¾Ð±ÑабоÑÑик click Ð´Ð»Ñ Ð¾ÑкÑÑÑÐ¸Ñ Ð¸ закÑÑÑÐ¸Ñ ÑпиÑка, и Ð¼ÐµÐ½Ñ <custom-menu> гоÑово:
customElements.define('custom-menu', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
// tmpl -- Ñаблон Ð´Ð»Ñ Ñеневого DOM-деÑева (вÑÑе)
this.shadowRoot.append( tmpl.content.cloneNode(true) );
// Ð¼Ñ Ð½Ðµ можем вÑбиÑаÑÑ ÑÐ·Ð»Ñ ÑвеÑлого DOM, поÑÑÐ¾Ð¼Ñ Ð¾Ð±ÑабоÑаем клики на ÑлоÑе
this.shadowRoot.querySelector('slot[name="title"]').onclick = () => {
// оÑкÑÑÑÑ/закÑÑÑÑ Ð¼ÐµÐ½Ñ
this.shadowRoot.querySelector('.menu').classList.toggle('closed');
};
}
});
ÐÐ¾Ñ Ð¿Ð¾Ð»Ð½Ð¾Ðµ демо:
ÐонеÑно, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑаÑÑиÑиÑÑ ÑÑнкÑионалÑноÑÑÑ Ð¼ÐµÐ½Ñ, добавив ÑобÑÑиÑ, меÑÐ¾Ð´Ñ Ð¸ Ñ.д.
Ðбновление ÑлоÑов
ЧÑо еÑли внеÑний код Ñ Ð¾ÑÐµÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸ÑеÑки добавиÑÑ Ð¸Ð»Ð¸ ÑдалиÑÑ Ð¿ÑнкÑÑ Ð¼ÐµÐ½Ñ?
ÐÑаÑÐ·ÐµÑ Ð½Ð°Ð±Ð»ÑÐ´Ð°ÐµÑ Ð·Ð° ÑлоÑами и обновлÑÐµÑ Ð¾ÑобÑажение пÑи добавлении и Ñдалении ÑлеменÑов в ÑлоÑÐ°Ñ .
Также, поÑколÑÐºÑ ÑÐ·Ð»Ñ ÑвеÑлого DOM-деÑева не копиÑÑÑÑÑÑ, а ÑолÑко оÑобÑажаÑÑÑÑ Ð² ÑлоÑÐ°Ñ , Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ð½ÑÑÑи Ð½Ð¸Ñ ÑÑÐ°Ð·Ñ Ð¶Ðµ ÑÑановÑÑÑÑ Ð²Ð¸Ð´Ð½Ñ.
Таким обÑазом, нам ниÑего не нÑжно делаÑÑ Ð´Ð»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾ÑобÑажениÑ. Ðо еÑли код компоненÑа Ñ
оÑÐµÑ ÑзнаÑÑ Ð¾Ð± изменениÑÑ
в ÑлоÑаÑ
, можно иÑполÑзоваÑÑ ÑобÑÑие slotchange.
ÐапÑимеÑ, здеÑÑ Ð¿ÑÐ½ÐºÑ Ð¼ÐµÐ½Ñ Ð²ÑÑавлÑеÑÑÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸ÑеÑки ÑеÑез 1 ÑекÑндÑ, и заголовок менÑеÑÑÑ ÑеÑез 2 ÑекÑндÑ:
<custom-menu id="menu">
<span slot="title">СладоÑÑи</span>
</custom-menu>
<script>
customElements.define('custom-menu', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `<div class="menu">
<slot name="title"></slot>
<ul><slot name="item"></slot></ul>
</div>`;
// shadowRoot не Ð¼Ð¾Ð¶ÐµÑ Ð¸Ð¼ÐµÑÑ Ð¾Ð±ÑабоÑÑиков ÑобÑÑий, поÑÑÐ¾Ð¼Ñ Ð¸ÑполÑзÑеÑÑÑ Ð¿ÐµÑвÑй поÑомок
this.shadowRoot.firstElementChild.addEventListener('slotchange',
e => alert("slotchange: " + e.target.name)
);
}
});
setTimeout(() => {
menu.insertAdjacentHTML('beforeEnd', '<li slot="item">ÐеденÑÑ</li>')
}, 1000);
setTimeout(() => {
menu.querySelector('[slot="title"]').innerHTML = "Ðовое менÑ";
}, 2000);
</script>
ÐÑобÑажение Ð¼ÐµÐ½Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÑеÑÑÑ ÐºÐ°Ð¶Ð´Ñй Ñаз без наÑего вмеÑаÑелÑÑÑва.
ÐдеÑÑ ÐµÑÑÑ Ð´Ð²Ð° ÑобÑÑÐ¸Ñ slotchange:
-
ÐÑи иниÑиализаÑии:
slotchange: titleзапÑÑкаеÑÑÑ ÑÑÐ°Ð·Ñ Ð¶Ðµ, как ÑолÑкоslot="title"из обÑÑного деÑева Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ Ð² ÑооÑвеÑÑÑвÑÑÑий ÑлоÑ. -
ЧеÑез 1 ÑекÑндÑ:
slotchange: itemзапÑÑкаеÑÑÑ, когда добавлÑеÑÑÑ Ð½Ð¾Ð²Ñй ÑлеменÑ<li slot="item">.
ÐбÑаÑиÑе внимание, ÑÑо ÑобÑÑие slotchange не запÑÑкаеÑÑÑ ÑеÑез 2 ÑекÑндÑ, когда менÑеÑÑÑ ÐºÐ¾Ð½ÑÐµÐ½Ñ slot="title". ÐÑо пÑоиÑÑ
Ð¾Ð´Ð¸Ñ Ð¿Ð¾ÑомÑ, ÑÑо Ñам ÑÐ»Ð¾Ñ Ð½Ðµ менÑеÑÑÑ. ÐÑ Ð¸Ð·Ð¼ÐµÐ½Ñем ÑодеÑжимое ÑлеменÑа, коÑоÑÑй наÑ
одиÑÑÑ Ð² ÑлоÑе, а ÑÑо ÑовÑем дÑÑгое.
ÐÑли Ð¼Ñ Ñ Ð¾Ñим оÑÑлеживаÑÑ Ð²Ð½ÑÑÑенние Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¾Ð±ÑÑного DOM-деÑева из JavaScript, можно Ñакже иÑполÑзоваÑÑ Ð±Ð¾Ð»ÐµÐµ обобÑÑннÑй Ð¼ÐµÑ Ð°Ð½Ð¸Ð·Ð¼: MutationObserver.
API ÑлоÑов
Ð, наконеÑ, давайÑе поговоÑим о меÑÐ¾Ð´Ð°Ñ JavaScript, ÑвÑзаннÑÑ Ñо ÑлоÑами.
Ðак Ð¼Ñ Ð²Ð¸Ð´ÐµÐ»Ð¸ ÑанÑÑе, JavaScript ÑмоÑÑÐ¸Ñ Ð½Ð° «ÑеалÑнÑй», а не на ÑазвÑÑнÑÑÑй DOM. Ðо еÑли Ñ Ñеневого деÑева ÑÑÐ¾Ð¸Ñ {mode: 'open'}, Ñо Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ вÑÑÑниÑÑ, какие ÑлеменÑÑ Ð½Ð°Ñ
одÑÑÑÑ Ð² ÑлоÑе, и, наобоÑоÑ, опÑеделиÑÑ ÑÐ»Ð¾Ñ Ð¿Ð¾ ÑлеменÑÑ, коÑоÑÑй в нÑм наÑ
одиÑÑÑ:
node.assignedSlotâ возвÑаÑÐ°ÐµÑ ÑлеменÑ<slot>, в коÑоÑом Ð½Ð°Ñ Ð¾Ð´Ð¸ÑÑÑnode.slot.assignedNodes({flatten: true/false})â DOM-ÑзлÑ, коÑоÑÑе Ð½Ð°Ñ Ð¾Ð´ÑÑÑÑ Ð² ÑлоÑе. ÐпÑиÑflattenÐ¸Ð¼ÐµÐµÑ Ð·Ð½Ð°Ñение по ÑмолÑаниÑfalse. ÐÑли Ñвно измениÑÑ Ð·Ð½Ð°Ñение наtrue, она пÑоÑмаÑÑÐ¸Ð²Ð°ÐµÑ ÑазвÑÑнÑÑÑй DOM глÑбже и возвÑаÑÐ°ÐµÑ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ñе ÑлоÑÑ, еÑли еÑÑÑ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ñе компоненÑÑ, и ÑезеÑвнÑй конÑенÑ, еÑли в ÑлоÑе Ð½ÐµÑ Ñзлов.slot.assignedElements({flatten: true/false})â DOM-ÑлеменÑÑ, коÑоÑÑе Ð½Ð°Ñ Ð¾Ð´ÑÑÑÑ Ð² ÑлоÑе (Ñо же Ñамое, ÑÑо вÑÑе, но ÑолÑко ÑзлÑ-ÑлеменÑÑ).
ÐÑи меÑÐ¾Ð´Ñ Ð¼Ð¾Ð¶Ð½Ð¾ иÑполÑзоваÑÑ Ð½Ðµ ÑолÑко Ð´Ð»Ñ Ð¾ÑобÑÐ°Ð¶ÐµÐ½Ð¸Ñ ÑодеÑжимого, коÑоÑое Ð½Ð°Ñ Ð¾Ð´Ð¸ÑÑÑ Ð² ÑлоÑÐ°Ñ , но и Ð´Ð»Ñ ÐµÐ³Ð¾ оÑÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð² JavaScript.
ÐапÑимеÑ, еÑли ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ <custom-menu> Ñ
оÑÐµÑ Ð·Ð½Ð°ÑÑ, ÑÑо он показÑваеÑ, он Ð¼Ð¾Ð¶ÐµÑ Ð¾ÑÑледиÑÑ ÑобÑÑие slotchange и полÑÑиÑÑ Ð¿ÑнкÑÑ Ð¼ÐµÐ½Ñ Ð¸Ð· slot.assignedElements:
<custom-menu id="menu">
<span slot="title">СладоÑÑи</span>
<li slot="item">ÐеденÑÑ</li>
<li slot="item">ФÑÑкÑовÑе ÑоÑÑÑ</li>
</custom-menu>
<script>
customElements.define('custom-menu', class extends HTMLElement {
items = []
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `<div class="menu">
<slot name="title"></slot>
<ul><slot name="item"></slot></ul>
</div>`;
// ÑлоÑовÑй ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÑеÑÑÑ/ÑдалÑеÑÑÑ/заменÑеÑÑÑ
this.shadowRoot.firstElementChild.addEventListener('slotchange', e => {
let slot = e.target;
if (slot.name == 'item') {
this.items = slot.assignedElements().map(elem => elem.textContent);
alert("Items: " + this.items);
}
});
}
});
// пÑнкÑÑ Ð¼ÐµÐ½Ñ Ð¾Ð±Ð½Ð¾Ð²ÑÑÑÑ ÑеÑез 1 ÑекÑндÑ
setTimeout(() => {
menu.insertAdjacentHTML('beforeEnd', '<li slot="item">ÐекÑÑ</li>')
}, 1000);
</script>
ÐÑого
ÐбÑÑно, еÑли Ñ ÑлеменÑа еÑÑÑ Ñеневое деÑево, Ñо ÑодеÑжимое обÑÑного, ÑвеÑлого DOM не показÑваеÑÑÑ. СлоÑÑ Ð¿Ð¾Ð·Ð²Ð¾Ð»ÑÑÑ Ð¿Ð¾ÐºÐ°Ð·Ð°ÑÑ ÑлеменÑÑ ÑвеÑлого DOM на заданнÑÑ Ð¼ÐµÑÑÐ°Ñ Ð² Ñеневом DOM.
СÑÑеÑÑвÑÐµÑ Ð´Ð²Ð° вида ÑлоÑов:
- ÐменованнÑе ÑлоÑÑ:
<slot name="X">...</slot>â полÑÑаÑÑ ÑлеменÑÑ ÑвеÑлого DOM Ñslot="X". - Ð¡Ð»Ð¾Ñ Ð¿Ð¾ ÑмолÑаниÑ: пеÑвÑй
<slot>без имени (поÑледÑÑÑие неименованнÑе ÑлоÑÑ Ð¸Ð³Ð½Ð¾ÑиÑÑÑÑÑÑ) â показÑÐ²Ð°ÐµÑ ÑлеменÑÑ ÑлеменÑов ÑвеÑлого деÑева, коÑоÑÑе не Ð½Ð°Ñ Ð¾Ð´ÑÑÑÑ Ð² дÑÑÐ³Ð¸Ñ ÑлоÑÐ°Ñ . - ÐÑли Ð¾Ð´Ð½Ð¾Ð¼Ñ ÑлоÑÑ Ð½Ð°Ð·Ð½Ð°Ñено неÑколÑко ÑлеменÑов, они добавлÑÑÑÑÑ Ð¾Ð´Ð¸Ð½ за дÑÑгим.
- СодеÑжимое ÑлеменÑа
<slot>иÑполÑзÑеÑÑÑ ÐºÐ°Ðº ÑезеÑвное. Ðно оÑобÑажаеÑÑÑ, еÑли в ÑлоÑе Ð½ÐµÑ ÑлеменÑов из ÑвеÑлого деÑева.
ÐÑоÑеÑÑ Ð¾ÑобÑÐ°Ð¶ÐµÐ½Ð¸Ñ ÑлеменÑов внÑÑÑи ÑлоÑа назÑваеÑÑÑ Â«ÐºÐ¾Ð¼Ð¿Ð¾Ð·Ð¸Ñией». Ð ÑезÑлÑÑаÑе композиÑии ÑÑÑоиÑÑÑ Â«ÑазвÑÑнÑÑÑй DOM».
ÐÑи композиÑии не пÑоиÑÑ Ð¾Ð´Ð¸Ñ Ð¿ÐµÑемеÑÐµÐ½Ð¸Ñ Ñзлов â Ñ ÑоÑки зÑÐµÐ½Ð¸Ñ JavaScript, DOM оÑÑаÑÑÑÑ Ð¿Ñежним.
JavaScript Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к ÑлоÑам Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ ÑледÑÑÑÐ¸Ñ Ð¼ÐµÑодов:
slot.assignedNodes/Elements()â возвÑаÑÐ°ÐµÑ ÑзлÑ/ÑлеменÑÑ, коÑоÑÑе Ð½Ð°Ñ Ð¾Ð´ÑÑÑÑ Ð²Ð½ÑÑÑиslot.node.assignedSlotâ обÑаÑнÑй меÑод, возвÑаÑÐ°ÐµÑ ÑÐ»Ð¾Ñ Ð¿Ð¾ ÑзлÑ.
ÐÑли Ð¼Ñ Ñ Ð¾Ñим знаÑÑ, ÑÑо показÑваем, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ оÑÑледиÑÑ ÐºÐ¾Ð½ÑÐµÐ½Ñ ÑлоÑа ÑледÑÑÑими ÑпоÑобами:
- ÑобÑÑие
slotchangeâ запÑÑкаеÑÑÑ, когда ÑÐ»Ð¾Ñ Ð½Ð°Ð¿Ð¾Ð»Ð½ÑеÑÑÑ ÐºÐ¾Ð½ÑенÑом в пеÑвÑй Ñаз, и пÑи каждой опеÑаÑии добавлениÑ/ÑдалениÑ/замеÑÐµÐ½Ð¸Ñ ÑлеменÑа в ÑлоÑе, за иÑклÑÑением его поÑомков. Сам ÑÐ»Ð¾Ñ Ð±ÑдеÑevent.target. - MutationObserver Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ глÑбокого пÑоÑмоÑÑа ÑодеÑжимого ÑлеменÑа в ÑлоÑе и оÑÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ в нÑм.
ТепеÑÑ, когда Ð¼Ñ Ð½Ð°ÑÑилиÑÑ Ð¿Ð¾ÐºÐ°Ð·ÑваÑÑ ÑлеменÑÑ ÑвеÑлого DOM в Ñеневом DOM, давайÑе поÑмоÑÑим, как Ð¸Ñ Ð¿ÑавилÑно ÑÑилизоваÑÑ. ÐÑновное пÑавило звÑÑÐ¸Ñ Ñак: ÑеневÑе ÑлеменÑÑ ÑÑилизÑÑÑÑÑ Ð²Ð½ÑÑÑи, а обÑÑнÑе ÑлеменÑÑ â ÑнаÑÑжи; однако еÑÑÑ Ð·Ð°Ð¼ÐµÑнÑе иÑклÑÑениÑ.
ÐÑ ÑаÑÑмоÑÑим Ð¸Ñ Ð¿Ð¾Ð´Ñобно в ÑледÑÑÑей главе.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)