Skip to content

Instantly share code, notes, and snippets.

@heyMP
Last active April 18, 2024 07:58
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save heyMP/8ef3912847dcc93304652a412981caca to your computer and use it in GitHub Desktop.
Save heyMP/8ef3912847dcc93304652a412981caca to your computer and use it in GitHub Desktop.
Recursively find elements through multiple layers of shadow dom.
/**
* Example usage:
* const hotspots = findAllDeep(element, `[slot*="hotspot"]`, 10);
*/
const findAllDeep = (parent, selectors, depth = null) => {
let nodes = new Set();
let currentDepth = 1;
const recordResult = (nodesArray) => {
for (const node of nodesArray) {
nodes.add(node)
}
}
const recursiveSeek = _parent => {
// check for selectors in lightdom
recordResult(_parent.querySelectorAll(selectors));
if (_parent.shadowRoot) {
// check for selectors in shadowRoot
recordResult(_parent.shadowRoot.querySelectorAll(selectors));
// look for nested components with shadowRoots
for (let child of [..._parent.shadowRoot.querySelectorAll('*')].filter(i => i.shadowRoot)) {
// make sure we haven't hit our depth limit
if (depth === null || currentDepth < depth) {
recursiveSeek(child);
}
}
}
};
recursiveSeek(parent);
return nodes;
};
@besserwisser
Copy link

Hey :) Thanks for this code. Could you add a license? I want to create a small npm package. I created a version that not just iterates over direct children shadow root, but instead the shadow roots can be everywhere as descendants. Also I removed some iterations you did in line 20 and I also added TypesScript.

By the way I don't think your "currentDepth" is working as expected. It is never incremented.

@ManuelKugelmann
Copy link

I completely reworked that to get the first matching nested element:

`

  const findDeep = (parent, selectors, depth = null) => {
  let currentDepth = 0;
  let result = null;
  
  const recursiveSeek = _parent => {

    currentDepth++;

    // check for selectors in lightdom
    result = _parent.querySelector(selectors);
    if(result)
      return result;

    // check for selectors in shadowRoot
    if (_parent.shadowRoot) {
      result = _parent.shadowRoot.querySelector(selectors);
      if(result)
        return result;
    }

    // look for nested components with shadowRoots

    let children = _parent.querySelectorAll('*');
    let childrenWithShadowRoot = [...children].filter(i => i.shadowRoot)
    for (let child of childrenWithShadowRoot) {
      if (depth === null || currentDepth < depth) {
        result = recursiveSeek(child.shadowRoot);
        if(result)
          return result;
      }
    }
    
    currentDepth--;
    return null;
    
  };
  
  return recursiveSeek(parent);
  
};

`

Usage:

let el = findDeep(document, '[style*="grid-area: main;"]', 100);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment