UNPKG

joicomponents

Version:

Design patterns to build native web components

100 lines (91 loc) 3.09 kB
<script> function log(e) { console.log(this.tagName, e.target.tagName + "#" + e.target.id); } class TheFather extends HTMLElement { constructor() { super(); this.attachShadow({mode: "open"}); this.shadowRoot.innerHTML = "<the-child><slot id='father'></slot></the-child>"; this.shadowRoot.addEventListener("slotchange", log.bind(this)); } } class TheChild extends HTMLElement { constructor() { super(); this.attachShadow({mode: "open"}); this.shadowRoot.innerHTML = "<slot id='child'></slot>"; this.shadowRoot.addEventListener("slotchange", log.bind(this)); } } customElements.define("the-father", TheFather); customElements.define("the-child", TheChild); </script> <the-father>matroska</the-father> <h3>Explanation: </h3> First, the flattened DOM looks like this: <pre> ... < the-father > #shadowRoot * slotchange listener < the-child > #shadowRoot * slotchange listener < slot#child > < slot#father > matroska </pre> Take note of two things: <ol> <li> The slot elements appear in reverse document order. </li> <li> The slotchange event listeners are attached to the #shadowRoot of each element. This causes the inner web component to be triggered first and then the outer web component. </li> <li> If the slotchange event listener were attached to the slot elements instead, then they would run in reverse document order. </li> </ol> Creating this DOM produces three logs: <pre> THE-CHILD SLOT#child THE-CHILD SLOT#father THE-FATHER SLOT#father </pre> <ol> <li> The first log output comes from slot#father being transposed into slot#child. The event start bubbling from the slot#child, is then caught by the event listener on the-child#shadowRoot, and finally stopped by the border between < the-child > host element and its #shadowRoot. <pre> ... < the-father > #shadowRoot < the-child > _ #shadowRoot *1 < slot#child > slotchange ^ < slot#father > matroska </pre> </li> <li> The second and third log output is produced by the same(!) slotchange event. The event occurs when the "matroska" text node is being transposed into slot#father. The event then bubbles up, and because it is "composed: false", it is stopped by the border between the-child and its #shadowRoot. However, because the slot elements remain in the flattened DOM in reverse document order as a SlotMatroska, the slotchange event listener on both the the-father and the-child element are triggered. Producing two outputs. <pre> ... < the-father > _ #shadowRoot *3 < the-child > | #shadowRoot *2 < slot#child > | < slot#father >slotchange ^ matroska </pre> </li> </ol>