2 min read

Shadow DOM

Shadow DOM is a web standard that offers component style and markup encapsulation. It is a critical piece of the Web Components story. It allows you to attach a hidden, separated DOM tree to an element.

1. Why do we need it?

In the standard DOM, everything is global.

  • CSS Leaks: A style defined for h1 affects every h1 on the page.
  • ID Conflicts: id="container" must be unique across the entire page.
  • Fragility: JavaScript can accidentally select elements inside a widget and break it.

Shadow DOM solves this by creating a "scoped" subtree.

2. Terminology

  • Shadow Host: The regular DOM node that the shadow DOM is attached to.
  • Shadow Tree: The DOM tree inside the Shadow DOM.
  • Shadow Boundary: The place where the shadow DOM ends, and the regular DOM begins.
  • Shadow Root: The root node of the shadow tree.

3. Creating Shadow DOM

You create a shadow root using element.attachShadow().

const host = document.querySelector('#host');
const shadowRoot = host.attachShadow({ mode: 'open' });

shadowRoot.innerHTML = `
  <style>
    p { color: red; } /* Only affects paragraphs inside this shadow root */
  </style>
  <p>I am in the shadows!</p>
`;

4. Encapsulation

CSS Isolation

Styles defined inside the Shadow DOM do not leak out. Styles defined outside do not leak in (mostly).

  • Exception: Inheritable properties (like color, font-family) still trickle down from the host.

DOM Isolation

document.querySelector('p') will not find the paragraph inside the shadow root. To access it, you must go through the shadow root: host.shadowRoot.querySelector('p').

5. Open vs. Closed Mode

When attaching a shadow root, you specify a mode.

  • open: You can access the shadow root using element.shadowRoot.
    const root = host.attachShadow({ mode: 'open' });
    console.log(host.shadowRoot); // Returns the ShadowRoot object
  • closed: element.shadowRoot returns null. This makes it harder (but not impossible) for outside JS to access the internal DOM.
    const root = host.attachShadow({ mode: 'closed' });
    console.log(host.shadowRoot); // null

    Note: Closed mode is rarely used because it prevents even your own code from easily accessing the root, and it doesn't offer true security.

6. Event Retargeting

When an event bubbles up from the Shadow DOM to the Light DOM (the regular page), its target is adjusted to look like it came from the Shadow Host.

  • Inside Shadow DOM: Target is the specific button clicked.
  • Outside Shadow DOM: Target is the <my-component> host element.

This preserves encapsulation; the outside world doesn't need to know about the internal structure.

programming/javascript/vanilla/javascript programming/javascript/vanilla/web-components programming/javascript/vanilla/dom-manipulation