Shadow DOM
On this page
Overview
The shadow DOM, together with slot elements, custom elements and templates compose the Web Components specification. Web components allow for easier creation and reuse of encapsulated components and possibly increased performance.
Encapsulation is achieved by using the shadow DOM, which is a a set of JS APIs which allow the creation of encapsulated DOM subtrees which can have their own style that has no effect outside the shadow DOM, while the elements inside do not get matched with rules outside the shadow DOM or in different shadow trees. This, along with shadow DOM specific differences, like querySelector
ignoring shadow subtrees, allows for creation of encapsulated components that can’t clash due to same ids or class names etc.
Performance gains can be seen in cases where complex selectors are used or styles that will change only a small portion of the elements are often altered. The performance benefits greatly depend on the page and the way the shadow DOM is used, for example a page with only inline styles will not benefit at all, while a page that heavily uses complex selectors will likely improve its performance significantly.
The slot element allows for creating encapsulated components, that can still be modified by the end user, it is a placeholder inside a shadow tree, which can be filled by the user of the component. The rendered subtree will look like the currently slotted elements have replaced the slot, while the DOM tree itself can remain unchanged.
Example
<style>
/* This will not match with the shadow element */
#idInShadow {
background-color: black;
}
</style>
<template id="custom-component-template">
<style>
/* This will not have an effect on elements outside the shadow DOM */
div {
background-color: green;
width: 100px;
height: 100px;
border-radius: 50%;
}
/* The slotted element will have its original styles + the styles from shadow dom ::slotted */
::slotted(*) {
background-color: red;
width: 50px;
height: 50px;
border-radius: 50%;
transform: translate(25px,25px);
}
</style>
<div id="idInShadow">
<slot>default slot value</slot>
</div>
</template>
<script>
const template = document.getElementById('custom-component-template');
const templadeNode = template.content.cloneNode(true);
customElements.define('custom-component', class extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'closed'}).appendChild(templadeNode.cloneNode(true));
}
});
</script>
<!-- can use the component and add any element which will rendered as if its in the place of <slot> in the shadow Tree-->
<custom-component>
<div></div>
</custom-component>
<custom-component>
<div style="background-color: blue;"></div>
</custom-component>
Supported subset
ShadowRoot
- The options
mode
,delegatesFocus
,slotAssignment
,clonable
host
innerHTML
activeElement
styleSheets
elementFromPoint
elementsFromPoint
- The options
- Slot element
- named assignment through
name
andslot
attributes - manual assignment through
assign()
- unnamed assignment
assignedNodes
assignedElements
- named assignment through
shadowRoot
attachShadow
- all options exceptserializable
assignedSlot
slotchange
event:host()
selector::slotted()
selector- Also added support for
all
shorthand property with value ofinitial
which allows for easy complete encapsulation from inherited styles by using:
:host { all: initial !important; }
Known issues
@keyframe
rules in shadow DOM get out of shadow encapsulation.- Slot element’s default display value is
block
instead ofcontents
as it is not supported. - Using
data-bind-for
anddata-bind-if
on subtrees that contain a shadow tree require it to be created withclonable: true
option to function correctly. For example, this will be needed when using these data-bind attributes on custom elements that useattachShadow
in their constructors.