Shadow DOM
The Shadow DOM is the most complicated part of Web Components because they offer a level of encapsulation and isolation that has never existed on the web platform.
html
<custom-alert>
<p>This is Light DOM content</p>
-----------Shadow DOM-------------
| <img src="path/to/icon.svg"/> |
| <slot></slot> |
| <button>Close</button> |
----------------------------------
</custom-alert>
<custom-alert>
<p>This is Light DOM content</p>
-----------Shadow DOM-------------
| <img src="path/to/icon.svg"/> |
| <slot></slot> |
| <button>Close</button> |
----------------------------------
</custom-alert>
The Shadow DOM behaves a lot like an <iframe>
, but better. Styling and scripting that happens within the Shadow DOM does not spill outside of its "shadow boundary". But three critical differences from an <iframe>
are:
- The Shadow DOM templates render as first-party content on the page.
- You avoid all the variable height issues related with
<iframe>
content. - Interacting with (open) Shadow DOM is much easier and doesn't have the
postMessage
workarounds or related security issues that exist with<iframe>
.
Attaching a Shadow Root
js
class CustomAlert extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open'|'closed' // default: 'closed'
delegatesFocus: true|false // default: false
})
}
}
class CustomAlert extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open'|'closed' // default: 'closed'
delegatesFocus: true|false // default: false
})
}
}
Shadow Roots have two modes
open
: Exposes thecustomElement.shadowRoot
to external JavaScriptclosed
:customElement.shadowRoot
returnsnull
to external JavaScript.
Appending a <template>
js
const myTemplate = document.createElement('template')
myTemplate.innerHTML = `<div>Hello, world!</div>`;
class CustomAlert extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.appendChild(myTemplate.content.cloneNode(true))
}
}
const myTemplate = document.createElement('template')
myTemplate.innerHTML = `<div>Hello, world!</div>`;
class CustomAlert extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.appendChild(myTemplate.content.cloneNode(true))
}
}
Resources
- Shadow DOM on MDN
- Web components: from zero to hero by Pascal Schilp