DEV Community

JS Bits Bill
JS Bits Bill

Posted on

Simplify Shadow DOM with setHTMLUnsafe

It's typical for Web developers to create encapsulated DOM elements in order to isolate both styles and functionality within a component. To achieve this, the element.attachShadow method is used to create a shadow DOM: a hidden subtree attached to an element. While powerful, it can be somewhat verbose and complex to set up:

const sectionElement = document.querySelector('section');
const shadowTemplate = document.createElement('template');
shadowTemplate.innerHTML = `
  <style>
    /* Shadow styles applied only within the shadow DOM */
    h2 { color: blue; }
  </style>
  <h2>Shadow Content</h2>
`;
shadowTemplate.content.shadowRootMode = 'open'
const shadowHost = document.createElement('div');
const shadowRoot = shadowHost.attachShadow({
  mode: 'open'
});
shadowRoot.appendChild(shadowTemplate.content.cloneNode(true));
sectionElement.appendChild(shadowHost);
sectionElement.insertAdjacentHTML('beforeend', `
  <h2>Light Content</h2>
`);
Enter fullscreen mode Exit fullscreen mode

This produces the following rendered HTML:

Image description

Notice how we must create the shadow DOM using the .attachShadow method first and then we can access the shadow root and use .innerHTML to populate the content.

Also note how .innerHTML cannot be used to directly create this output. This method is not designed to understand shadow elements within the template. Further, it also prevents this content from rendering due to a browser security restriction. If user-generated content is inserted with .innerHTML, it could contain malicious scripts that execute within your page. Shadow DOM creates a separate DOM tree that prevents styles and scripts from leaking into the main page.

However, if we understand and accept the security implications, we can take advantage of the new setHTMLUnsafe method to directly set HTML content, including shadow DOM declarations, bypassing the need for separate shadow DOM attachment methods like .attachShadow:

document.querySelector('section').setHTMLUnsafe(`
  <div>
    <template shadowrootmode="open">
      <style>
        /* Shadow styles applied only within the shadow DOM */
        h2 { color: blue; }
      </style>
      <h2>Shadow Content</h2>
    </template>
  </div>
  <h2>Light Content</h2>
`)
Enter fullscreen mode Exit fullscreen mode

With setHTMLUnsafe, you don't need to manage separate template elements, shadow hosts, or shadow roots manually. This can reduce the complexity of your codebase and make it easier to maintain, especially in larger projects where encapsulation of DOM elements is a common practice.

setHTMLUnsafe is available now in Chrome 124 (and supported in Edge, Safari, and Firefox).

Top comments (0)