许å¤ç±»åçç»ä»¶ï¼ä¾å¦æ ç¾ãèåãç §çåºççï¼éè¦å 容廿¸²æã
就念è§å¨å
建ç <select> éè¦ <option> åé¡¹ï¼æä»¬ç <custom-tabs> å¯è½éè¦å®é
çæ ç¾å
容æ¥èµ·ä½ç¨ãå¹¶ä¸ä¸ä¸ª <custom-menu> å¯è½éè¦èåå项ã
使ç¨äº <custom-menu> ç代ç å¦ä¸æç¤ºï¼
<custom-menu>
<title>Candy menu</title>
<item>Lollipop</item>
<item>Fruit Toast</item>
<item>Cup Cake</item>
</custom-menu>
â¦â¦ä¹åï¼æä»¬çç»ä»¶åºè¯¥æ£ç¡®å°æ¸²ææå ·æç»å®æ é¢å项ç®ãå¤çèåäºä»¶ççæ¼äº®èåã
å¦ä½å®ç°å¢ï¼
æä»¬å¯ä»¥å°è¯åæå ç´ å 容并卿å¤å¶éæ°æå DOM èç¹ãè¿æ¯å¯è½çï¼ä½æ¯å¦ææä»¬è¦å°å ç´ ç§»å¨å° Shadow DOMï¼é£ä¹ææ¡£ç CSS æ ·å¼ä¸è½å¨é£éåºç¨ï¼å æ¤ææ¡£çè§è§æ ·å¼å¯è½ä¼ä¸¢å¤±ãçèµ·æ¥è¿éè¦åä¸äºäºæ ã
幸è¿çæ¯æä»¬ä¸éè¦å»åãShadow DOM æ¯æ <slot> å
ç´ ï¼ç± light DOM ä¸çå
容èªå¨å¡«å
ã
å ·åææ§½
让æä»¬éè¿ä¸ä¸ªç®åçä¾åçä¸ææ§½æ¯å¦ä½å·¥ä½çã
å¨è¿é <user-card> shadow DOM æä¾ä¸¤ä¸ªææ§½, ä» light DOM å¡«å
ï¼
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<div>Name:
<slot name="username"></slot>
</div>
<div>Birthday:
<slot name="birthday"></slot>
</div>
`;
}
});
</script>
<user-card>
<span slot="username">John Smith</span>
<span slot="birthday">01.01.2001</span>
</user-card>
å¨ shadow DOM ä¸ï¼<slot name="X"> å®ä¹äºä¸ä¸ªâæå
¥ç¹âï¼ä¸ä¸ªå¸¦æ slot="X" çå
ç´ è¢«æ¸²æçå°æ¹ã
ç¶åæµè§å¨æ§è¡âç»åâï¼å®ä» light DOM ä¸è·åå ç´ å¹¶ä¸æ¸²æå° shadow DOM ä¸çå¯¹åºææ§½ä¸ãæåï¼æ£æ¯æä»¬æ³è¦ç ââ ä¸ä¸ªè½è¢«å¡«å æ°æ®çéç¨ç»ä»¶ã
è¿æ¯ç¼è¯åï¼ä¸èèç»åç DOM ç»æï¼
<user-card>
#shadow-root
<div>Name:
<slot name="username"></slot>
</div>
<div>Birthday:
<slot name="birthday"></slot>
</div>
<span slot="username">John Smith</span>
<span slot="birthday">01.01.2001</span>
</user-card>
æä»¬åå»ºäº shadow DOMï¼æä»¥å®å½ç¶å°±åå¨äºï¼ä½äº #shadow-root ä¹ä¸ãç°å¨å
ç´ åæ¶æ¥æ light DOM å shadow DOMã
ä¸ºäºæ¸²æ shadow DOM ä¸çæ¯ä¸ä¸ª <slot name="..."> å
ç´ ï¼æµè§å¨å¨ light DOM ä¸å¯»æ¾ç¸åååç slot="..."ãè¿äºå
ç´ å¨ææ§½å
被渲æï¼
ç»æè¢«å«åæå¹³åï¼flattenedï¼DOMï¼
<user-card>
#shadow-root
<div>Name:
<slot name="username">
<!-- slotted element is inserted into the slot -->
<span slot="username">John Smith</span>
</slot>
</div>
<div>Birthday:
<slot name="birthday">
<span slot="birthday">01.01.2001</span>
</slot>
</div>
</user-card>
â¦â¦ä½æ¯ âflattenedâ DOM ä» ä» è¢«åå»ºç¨æ¥æ¸²æåäºä»¶å¤çï¼æ¯âèæâçãè½ç¶æ¯æ¸²æåºæ¥äºï¼ä½ææ¡£ä¸çèç¹äºå®ä¸å¹¶æ²¡æå°å¤ç§»å¨ï¼
妿æä»¬è°ç¨ querySelectorAll é£å°±å¾å®¹æéªè¯ï¼èç¹ä»å¨å®ä»¬çä½ç½®ã
// light DOM <span> èç¹ä½ç½®ä¾ç¶ä¸åï¼å¨ `<user-card>` é
alert( document.querySelectorAll('user-card span').length ); // 2
å æ¤ï¼æå¹³å DOM æ¯éè¿æå ¥ææ§½ä» shadow DOM æ´¾çåºæ¥çãæµè§å¨æ¸²æå®å¹¶ä¸ç¨äºæ ·å¼ç»§æ¿ãäºä»¶ä¼ æã使¯ JavaScript 卿平åä»æåæ ·çå°ææ¡£ã
slot="..." 屿§ä»
ä»
对 shadow host çç´æ¥å代 (卿们çä¾åä¸ç <user-card> å
ç´ ) ææã对äºåµå¥å
ç´ å®å°è¢«å¿½ç¥ã
ä¾å¦ï¼è¿éç第äºä¸ª <span> 被忽ç¥äº(å 为å®ä¸æ¯ <user-card> çé¡¶å±åå
ç´ )ï¼
<user-card>
<span slot="username">John Smith</span>
<div>
<!-- invalid slot, must be direct child of user-card -->
<span slot="birthday">01.01.2001</span>
</div>
</user-card>
å¦æå¨ light DOM éæå¤ä¸ªç¸åææ§½åçå ç´ ï¼é£ä¹å®ä»¬ä¼è¢«ä¸ä¸ªæ¥ä¸ä¸ªå°æ·»å å°ææ§½ä¸ã
ä¾å¦è¿æ ·ï¼
<user-card>
<span slot="username">John</span>
<span slot="username">Smith</span>
</user-card>
ç»è¿ä¸ªæå¹³å DOM 两个å
ç´ ï¼æå
¥å° <slot name="username"> éï¼
<user-card>
#shadow-root
<div>Name:
<slot name="username">
<span slot="username">John</span>
<span slot="username">Smith</span>
</slot>
</div>
<div>Birthday:
<slot name="birthday"></slot>
</div>
</user-card>
ææ§½åå¤å 容
妿æä»¬å¨ä¸ä¸ª <slot> å
鍿¾ç¹ä»ä¹ï¼å®å°æä¸ºåå¤å
容ã妿 light DOM 䏿²¡æç¸åºå¡«å
ç©çè¯æµè§å¨å°±å±ç¤ºå®ã
ä¾å¦ï¼å¨è¿éç shadow DOM ä¸ï¼å¦æ light DOM 䏿²¡æ slot="username" çè¯ Anonymous 就被渲æã
<div>Name:
<slot name="username">Anonymous</slot>
</div>
é»è®¤ææ§½ï¼ç¬¬ä¸ä¸ªä¸å ·åçææ§½
shadow DOM ä¸ç¬¬ä¸ä¸ªæ²¡æååç <slot> æ¯ä¸ä¸ªé»è®¤ææ§½ãå®ä» light DOM ä¸è·åæ²¡ææ¾ç½®å¨å
¶ä»ä½ç½®çææèç¹ã
ä¾å¦ï¼è®©æä»¬æé»è®¤ææ§½æ·»å å° <user-card>ï¼è¯¥ä½ç½®å¯ä»¥æ¶éæå
³ç¨æ·çæææªå¼æ§½ï¼unslottedï¼çä¿¡æ¯ï¼
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<div>Name:
<slot name="username"></slot>
</div>
<div>Birthday:
<slot name="birthday"></slot>
</div>
<fieldset>
<legend>Other information</legend>
<slot></slot>
</fieldset>
`;
}
});
</script>
<user-card>
<div>I like to swim.</div>
<span slot="username">John Smith</span>
<span slot="birthday">01.01.2001</span>
<div>...And play volleyball too!</div>
</user-card>
æææªè¢«æå ¥ç light DOM å 容è¿å ¥ âå ¶ä»ä¿¡æ¯â åæ®µéã
å ç´ ä¸ä¸ªæ¥ä¸ä¸ªçéå å°ææ§½ä¸ï¼å æ¤è¿ä¸¤ä¸ªæªæå ¥ææ§½çä¿¡æ¯é½å¨é»è®¤ææ§½ä¸ã
æå¹³åç DOM çèµ·æ¥åè¿æ ·ï¼
<user-card>
#shadow-root
<div>Name:
<slot name="username">
<span slot="username">John Smith</span>
</slot>
</div>
<div>Birthday:
<slot name="birthday">
<span slot="birthday">01.01.2001</span>
</slot>
</div>
<fieldset>
<legend>About me</legend>
<slot>
<div>Hello</div>
<div>I am John!</div>
</slot>
</fieldset>
</user-card>
Menu example
ç°å¨è®©æä»¬åå°å¨æ¬ç« å¼å¤´æå°ç <custom-menu> ã
æä»¬å¯ä»¥ä½¿ç¨ææ§½æ¥åé å ç´ ã
è¿æ¯ <custom-menu>ï¼
<custom-menu>
<span slot="title">Candy menu</span>
<li slot="item">Lollipop</li>
<li slot="item">Fruit Toast</li>
<li slot="item">Cup Cake</li>
</custom-menu>
带æéå½ææ§½ç shadow DOM 模çï¼
<template id="tmpl">
<style> /* menu styles */ </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">ãå æ¤ææå¸¦æslot="item"çå ç´ é½ä¸ä¸ªæ¥ä¸ä¸ªå°éå å°<slot name="item">ä¸ï¼ä»èå½¢æå表ã
æå¹³åç DOM å为ï¼
<custom-menu>
#shadow-root
<style> /* menu styles */ </style>
<div class="menu">
<slot name="title">
<span slot="title">Candy menu</span>
</slot>
<ul>
<slot name="item">
<li slot="item">Lollipop</li>
<li slot="item">Fruit Toast</li>
<li slot="item">Cup Cake</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 is the shadow DOM template (above)
this.shadowRoot.append( tmpl.content.cloneNode(true) );
// we can't select light DOM nodes, so let's handle clicks on the slot
this.shadowRoot.querySelector('slot[name="title"]').onclick = () => {
// open/close the menu
this.shadowRoot.querySelector('.menu').classList.toggle('closed');
};
}
});
è¿æ¯å®æ´çæ¼ç¤ºï¼
å½ç¶æä»¬å¯ä»¥ä¸ºå®æ·»å æ´å¤çåè½ï¼äºä»¶ãæ¹æ³çã
æ´æ°ææ§½
妿å¤é¨ä»£ç æ³å¨æ æ·»å /ç§»é¤ èå项æä¹åï¼
妿 æ·»å /å é¤ äºææ§½å ç´ ï¼æµè§å¨å°çè§ææ§½å¹¶æ´æ°æ¸²æã
å¦å¤ï¼ç±äºä¸å¤å¶ light DOM èç¹ï¼èæ¯ä» å¨ææ§½ä¸è¿è¡æ¸²æï¼æä»¥å é¨çå忝ç«å³å¯è§çã
å æ¤æä»¬æ éæ§è¡ä»»ä½æä½å³å¯æ´æ°æ¸²æã使¯å¦æç»ä»¶æ³ç¥éææ§½çæ´æ¹ï¼é£ä¹å¯ä»¥ç¨ slotchange äºä»¶ã
ä¾å¦ï¼è¿éçèåé¡¹å¨ 1 ç§å卿æå ¥ï¼è䏿 é¢å¨ 2 ç§åæ¹åã
<custom-menu id="menu">
<span slot="title">Candy menu</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 can't have event handlers, so using the first child
this.shadowRoot.firstElementChild.addEventListener('slotchange',
e => alert("slotchange: " + e.target.name)
);
}
});
setTimeout(() => {
menu.insertAdjacentHTML('beforeEnd', '<li slot="item">Lollipop</li>')
}, 1000);
setTimeout(() => {
menu.querySelector('[slot="title"]').innerHTML = "New menu";
}, 2000);
</script>
èåæ¯æ¬¡é½ä¼æ´æ°æ¸²æèæ éæä»¬å¹²é¢ã
è¿éæä¸¤ä¸ª slotchange äºä»¶ï¼
-
å¨åå§åæ¶:
slotchange: titleç«å³è§¦å, å 为æ¥èª light DOM çslot="title"è¿å ¥äºç¸åºçææ§½ã -
1 ç§å:
slotchange: item触å, å½ä¸ä¸ªæ°ç<li slot="item">被添å ã
请注æï¼2 ç§åï¼å¦æä¿®æ¹äº slot="title" çå
容ï¼åä¸ä¼åç slotchange äºä»¶ãå ä¸ºæ²¡æææ§½æ´æ¹ãæä»¬ä¿®æ¹äº slotted å
ç´ çå
容ï¼è¿æ¯å¦ä¸åäºã
妿æä»¬æ³éè¿ JavaScript è·è¸ª light DOM çå é¨ä¿®æ¹ï¼ä¹å¯ä»¥ä½¿ç¨æ´éç¨çæºå¶: MutationObserverã
ææ§½ API
æå让æä»¬æ¥è°è°ä¸ææ§½ç¸å ³ç JavaScript æ¹æ³ã
æ£å¦æä»¬ä¹åæè§ï¼JavaScript 伿¥ççå®ç DOMï¼ä¸å±å¼ã使¯å¦æ shadow æ æ {mode: 'open'} ï¼é£ä¹æä»¬å¯ä»¥æ¾åºåªä¸ªå
ç´ è¢«æ¾è¿ä¸ä¸ªææ§½ï¼åä¹äº¦ç¶ï¼åªä¸ªææ§½åé
äºç»è¿ä¸ªå
ç´ ï¼
node.assignedSlotâ è¿ånodeåé ç»ç<slot>å ç´ ã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">Candy menu</span>
<li slot="item">Lollipop</li>
<li slot="item">Fruit Toast</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);
}
});
}
});
// items å¨ 1 ç§åæ´æ°
setTimeout(() => {
menu.insertAdjacentHTML('beforeEnd', '<li slot="item">Cup Cake</li>')
}, 1000);
</script>
å°ç»
é常ï¼å¦æä¸ä¸ªå ç´ å«æ shadow DOMï¼é£ä¹å ¶ light DOM å°±ä¸ä¼è¢«å±ç¤ºåºæ¥ãææ§½å è®¸å¨ shadow DOM 䏿¾ç¤º light DOM åå ç´ ã
ææ§½æä¸¤ç§ï¼
- å
·åææ§½ï¼
<slot name="X">...</slot>â 使ç¨slot="X"è·å light åå ç´ ã - é»è®¤ææ§½ï¼ç¬¬ä¸ä¸ªæ²¡æååç
<slot>ï¼éåçæªå½åææ§½å°è¢«å¿½ç¥ï¼- æ¥å䏿¯ææ§½ç light åå ç´ ã - 妿åä¸ææ§½ä¸æå¾å¤å ç´ â å®ä»¬ä¼è¢«ä¸ä¸ªæ¥ä¸ä¸ªå°æ·»å ã
<slot>å ç´ çå 容ä½ä¸ºå¤ç¨ãå¦æææ§½æ²¡æ light åçåå ç´ ï¼å°±ä¼æ¾ç¤ºã
å¨å ¶ææ§½å æ¸²æææ§½å ç´ çè¿ç¨ç§°ä¸ºâç»åâãç»æç§°ä¸ºâæå¹³å DOMâã
ç»åä¸ä¼çå®çå»ç§»å¨èç¹ï¼ä» JavaScript çè§è§ç DOM ä»ç¶æ¯ç¸åçã
JavaScript å¯ä»¥ä½¿ç¨ä»¥ä¸çæ¹æ³è®¿é®ææ§½ï¼
slot.assignedNodes/Elements()â è¿åææ§½å ç èç¹/å ç´ ãnode.assignedSlotâ ç¸åçæ¹æ³ï¼è¿åä¸ä¸ªèç¹çææ§½ã
妿æä»¬æ³ç¥éæ¾ç¤ºçå 容ï¼å¯ä»¥ä½¿ç¨ä»¥ä¸æ¹æ³è·è¸ªææ§½ä½çå 容ï¼
slotchangeäºä»¶ â å¨ææ§½ç¬¬ä¸æ¬¡å¡«å æ¶è§¦åï¼å¹¶ä¸å¨ææ§½å ç´ ç æ·»å /å é¤/æ¿æ¢ æä½ï¼è䏿¯å ¶åå ç´ ï¼æ¶è§¦åï¼ææ§½æ¯event.targetã- ä½¿ç¨ MutationObserver æ¥æ·±å ¥äºè§£ææ§½å 容ï¼å¹¶æ¥çå ¶ä¸çæ´æ¹ã
ç°å¨ï¼å¨ shadow DOM 䏿æ¥èª light DOM çå ç´ æ¶ï¼è®©æä»¬ççå¦ä½æ£ç¡®çè®¾ç½®æ ·å¼ãåºæ¬è§åæ¯ shadow å ç´ å¨å é¨è®¾ç½®æ ·å¼ï¼light å ç´ å¨å¤é¨è®¾ç½®æ ·å¼ï¼ä½æ¯æä¸äºä¾å¤ã
æä»¬å°å¨ä¸ä¸ç« ä¸çå°è¯¦ç»å 容ã
è¯è®º
<code>æ ç¾æå ¥åªæå 个è¯ç代ç ï¼æå ¥å¤è¡ä»£ç å¯ä»¥ä½¿ç¨<pre>æ ç¾ï¼å¯¹äºè¶ è¿ 10 è¡ç代ç ï¼å»ºè®®ä½ ä½¿ç¨æ²ç®±ï¼plnkrï¼JSBinï¼codepenâ¦ï¼