joicomponents
Version:
Design patterns to build native web components
135 lines (129 loc) • 3.87 kB
HTML
<script>
class PortraitFrame extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.innerHTML = `
<style>
:host(*){
border: 10px solid darkgreen;
width: 300px;
height: 300px;
display: block;
}
green-frame {
box-sizing: border-box;
display: block;
width: 100%;
height: 100%;
}
</style>
<green-frame id="frameOne">
<slot id="outer">a picture</slot>
</green-frame>`;
}
}
class GreenFrame extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.innerHTML = `
<style>
:host {
border: 4px solid green;
}
</style>
<slot id="inner"></slot>`;
this.shadowRoot.addEventListener("slotchange", function (e) {
console.log(e.composedPath(), e.target.assignedElements(), e.target.assignedElements({flatten: true}));
});
}
}
customElements.define("green-frame", GreenFrame);
customElements.define("portrait-frame", PortraitFrame);
</script>
<style> pre { margin: 0 } </style>
<portrait-frame id="portrait">
<pre>¯\_(ツ)_/¯</pre>
<pre> | | </pre>
<pre> | 6 | </pre>
<script>var take = "a break";</script>
<pre> === </pre>
<script>var take5;</script>
<pre> | | </pre>
<pre> | | | </pre>
<pre> | | | </pre>
<pre> ^ ^ </pre>
</portrait-frame>
<h3>SlotchangeExplosion #2</h3>
<p>
When we view this situation from HTML, it looks the same as in our previous example SlotchangeExplosion #1.
The only difference is that two empty scripts will be processed along the way.
But, in your console, you should now see 4 (or more) logs. Why is that?
</p>
<p>
The first script deliberately causes the browser to break its creation of the DOM.
This means that the browser will make a "temporary" DOM branch that looks like this:
<pre>
...
< portrait-frame >
#shadowRoot
..
< green-frame >
#shadowRoot
< slot#inner >
< slot#outer >
< pre >¯\_(ツ)_/¯
< pre > | |
---break 1---
</pre>
This is the temporary state of the DOM in which the second slotchange event is dispatched.
And you can see this state in the slot#outer.assignedElements({flatten: true}) which is:
`[pre, pre, pre, script]`
</p>
<p>
The second script does the same with a "temporary" DOM branch that looks like this:
<pre>
...
< portrait-frame >
#shadowRoot
..
< green-frame >
#shadowRoot
< slot#inner >
< slot#outer >
< pre >¯\_(ツ)_/¯
< pre > | |
< pre > | 6 |
---break 2---
</pre>
This is the temporary state of the DOM in which the third slotchange event is dispatched.
And you can see this state in the slot#outer.assignedElements({flatten: true}) which is:
`[pre, pre, pre, script, pre, script]`
</p>
<p>
The last slotchange event occurs when the DOM branch is complete.
<pre>
...
< portrait-frame >
#shadowRoot
..
< green-frame >
#shadowRoot
< slot#inner >
< slot#outer >
< pre >¯\_(ツ)_/¯
< pre > | |
< pre > | 6 |
< pre > ===
< pre > | |
< pre > | | |
< pre > | | |
< pre > ^ ^
</pre>
</p>
<p>
You do not have absolute control of when the browser wishes to break off and display the DOM.
The browser can choose to break divide the view up in ways similar to our useless scripts.
This can cause the number of slotchange events you get during testing and development to change.
</p>