liveBook · Manning

Shadow root

As explained in Web Components in Action

A shadow root is the root node of an encapsulated Shadow DOM subtree attached to a host element, with open or closed access modes.

Suppose a custom element contains a `<div>` that page code can find and change. A shadow root gives the element a separate DOM tree for its internal content. The element is the shadow host; the root sits at the boundary between that host and the Shadow DOM. Like a document fragment, it is separate from the page’s main DOM tree. section 8.2

Listing 8.2. Using the Shadow DOM in a simple component
connectedCallback() {
   this.attachShadow({mode: 'open'});                       #1
   this.shadowRoot.innerHTML =
   `<div class="inside-component">My Component</div>`       #2
}

The minimal open-mode example attaches a root, then puts markup inside it through `this.shadowRoot`. section 8.2

Pass a mode when calling `attachShadow`: `open` or `closed`. With an open root, `this.shadowRoot` provides access from the component, and outside code can reach inside by first selecting the host. A page-level `document.querySelector('.inside-component')` alone will not cross the boundary. So open mode changes ordinary DOM access; it does not make the internals inaccessible. section 8.2

Keep a reference to a closed root

To build inside a closed root, retain the value returned by `attachShadow({mode: 'closed'})`: `const root = this.attachShadow({mode: 'closed'}); root.innerHTML = '<div>My Component</div>';`. Do not use `this.shadowRoot` for that initialization. Closed mode limits normal access, but it is not a security boundary against determined interference. section 8.2

A shadow root also makes slots useful. Attach it to a custom element, append a template containing named `<slot>` elements, then put matching `slot` attributes on content inside the element’s tag. An unfilled slot displays its fallback content. This lets the element keep its layout while accepting values supplied by its user. section 7.5

The boundary matters for CSS too: ordinary page-level selectors do not style matching elements inside the root, and internal rules do not creep into other shadow trees. To style the host from inside, use `:host` rather than the custom element’s tag name. Encapsulation has nuance, though: inherited text styling can still affect content inside. section 9.2

Need a non-Shadow-DOM fallback? Store either the attached root or the host itself in one `root` property. Move DOM-dependent initialization to `connectedCallback`: without a shadow root, the page-provided DOM is not yet available in the constructor. Selectors and host styles may also need corresponding changes. section 12.1

How Web Components in Action covers it

How deep it goes
Works through attachment, open and closed access, querying, and fallback initialization; cautions that closed mode does not stop malicious interference.
Examples
Uses a minimal custom element, a slotted business card, and a component with isolated CSS.
What it assumes
Assumes familiarity with HTML, DOM queries, and custom elements while explaining the shadow root through document fragments and small code changes.
What you can do after
Build it from scratch with `attachShadow`, choose an access mode, and retain a root reference when needed.
How it connects
Connects shadow roots to custom elements, document fragments, slots, Shadow DOM, and the `:host` CSS selector.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Web Components in Action. The section links point to the parts of the book it draws on.