Extracting the Flattened, Rendered HTML of Web Components

If you work with or test Web Components you already know the pain of inspecting them. You select a custom element in Chrome DevTools, right click, hit copy outerHTML, and you are left with an unhelpful string of empty tags.

DevTools strictly separates the Light DOM from the Shadow DOM. If you want the actual Composed Tree (a single, flattened HTML string representing exactly what the browser paints on the screen, where Shadow DOMs are expanded and slot tags are physically replaced by their assigned nodes) DevTools offers absolutely no native way to do it.

To get this snapshot for inspection or debugging, you have to traverse the DOM exactly like the browser rendering engine does.

Kind Of A Solution

Here is a script that grabs your currently selected element, pierces all open shadow roots, replaces slots with their projected content, drops invisible nodes, and copies the fully flattened HTML directly to your clipboard.

copy((() => {
  const target = (typeof $0 !== 'undefined' && $0) ? $0 : document.body;

  function getFlattenedHTML(node) {
    if (node.nodeType === Node.TEXT_NODE) return node.textContent;
    if (node.nodeType === Node.COMMENT_NODE) return ``;
    if (node.nodeType !== Node.ELEMENT_NODE) return '';

    if (node.tagName.toLowerCase() === 'slot') {
      const assigned = node.assignedNodes({ flatten: true });
      if (assigned.length > 0) {
        return assigned.map(n => getFlattenedHTML(n)).join('');
      } else {
        return Array.from(node.childNodes).map(n => getFlattenedHTML(n)).join('');
      }
    }

    const clone = node.cloneNode(false);
    let outer = clone.outerHTML;

    const childrenToWalk = node.shadowRoot ? node.shadowRoot.childNodes : node.childNodes;

    let childrenHTML = '';
    childrenToWalk.forEach(child => {
      childrenHTML += getFlattenedHTML(child);
    });

    if (childrenHTML) {
      const closeTagIndex = outer.lastIndexOf('</');
      if (closeTagIndex !== -1) {
        return outer.slice(0, closeTagIndex) + childrenHTML + outer.slice(closeTagIndex);
      }
    }
    return outer;
  }

  return getFlattenedHTML(target);
})());

How to use it

Because this relies on DevTools specific commands like copy and the selected element variable, the best way to use this is as a DevTools Snippet.

  1. Open DevTools and go to the Sources tab.
  2. Open the Snippets pane on the left side.
  3. Click New snippet, name it “Flatten DOM”, paste the code, and save.

To run it on demand:

  1. Select your target element in the DevTools Elements panel.
  2. Open the DevTools Command Menu.
  3. Type !Flatten DOM and hit Enter.

Your clipboard now contains the fully rendered HTML tree.

I would love to use a bookmarklet for this, but it added some challenges I did not need for this short thing. But if you have an idea how to improve this, I am all ears!

Cheers!

Portrait of Moritz Glantz

👋 Moritz Glantz is my name…

…and I am an Specialist for Web Accessibility and UX-Engineer with over 20 years of experience, by day and a podcaster by night.

I help organizations build accessible websites by offering talks about accessibility, workshops & trainings, accessibility reviews and developing strategies for developers and management.

If that sounds like you could use my help, let’s talk! 💬