12

I need get elements from Shadow DOM and change it. How i can do it?

<div>
     <input type="range" min="100 $" max="3000 $">
</div>
Supersharp
  • 26,125
  • 8
  • 76
  • 119
Yaroslav Polubedov
  • 1,182
  • 2
  • 10
  • 13

3 Answers3

30

You cannot access a Shadow DOM created by the browser to display a control, that is called a #shadow-root (user-agent) in the Dev Tools. <input> is one example.

You can only access open custom Shadow DOM (the ones that you create yourself), with the { mode: 'open' } option.

element.attachShadow( { mode: 'open' } )

Update

It's true for most UX standard HTML elements: <input>, <video>, <textarea>, <select>, <audio>, etc.

Supersharp
  • 26,125
  • 8
  • 76
  • 119
  • 1
    I know that this post was 2 years ago, but do you have the source of this? Or are there any updates about this? I am trying to access the shadow dom that created by the iOS safari. Thanks. – He Wang Aug 03 '18 at 12:41
  • 2
    Source of what? user-agent Shadow DOM are browser vendors native implementation so they are not documented and will never be accessible. Only open Shadow DOM are, as per the specs. – Supersharp Aug 03 '18 at 15:37
10

Here is an example:

var container = document.querySelector('#example');
//Create shadow root !
var root = container.createShadowRoot();
root.innerHTML = '<div>Root<div class="test">Element in shadow</div></div>';

//Access the element inside the shadow !
//"container.shadowRoot" represents the youngest shadow root that is hosted on the element !
console.log(container.shadowRoot.querySelector(".test").innerHTML);

Demo:

var container = document.querySelector('#example');
//Create shadow root !
var root = container.createShadowRoot();
root.innerHTML = '<div>Root<div class="test">Element in shadow</div></div>';

//Access the element inside the shadow !
console.log(container.shadowRoot.querySelector(".test").innerHTML);
<div id="example">Element</div>

I hope this will help you.

Ismail RBOUH
  • 9,836
  • 2
  • 22
  • 36
  • 1
    Thank you! But element has ShadowDOM with
    , and I need get this element.
    – Yaroslav Polubedov Aug 01 '16 at 15:05
  • The shadow root created on DIV ? because it can be created on the Input ! – Ismail RBOUH Aug 01 '16 at 16:33
  • This doesn't seem to work in recent Firefox: `"message": "TypeError: container.createShadowRoot is not a function"` – Jolta Mar 12 '19 at 10:20
  • 2
    Downvoted as this described creating a shadow root with the Javascript API, it does not describe access the browser-implemented shadow root. The response indicating you cannot access the shadow root generated by the browser (e.g. #shadow-root in Chrome) is more correct. However, thank you for explaining creating one as well. – BradGreens Sep 09 '19 at 17:46
  • 2
    This does not answer how to access user-agent element's shadow-root (already created by browser). It shows how to access custom element's shadow-root (created by developer). – Pablo Jun 05 '20 at 13:22
  • `createShadowRoot` is non-standard and deprecated. Neither Firefox nor Safari support it. **Don't use it.** Instead use `element.attachShadow({mode: 'open'});`. – connexo Apr 24 '22 at 14:40
2

To answer (a generalized version of) the OP question:

How can you query elements, no matter if in shadowRoot or not?

It feels like the shadow root API is still lacking (or I just don't know it well enough). But it basically renders querySelectorAll somewhat useless, in that querySelectorAll will not actually get all matching elements anymore, since it simply ignores all ancestors in shadowRoots. Maybe there is an API that fixes that, but just in case there is not:

Here is a simple function that recursively iterates all shadowRoots and gets you all matching elements on the page, not just those of a single shadowRoot.

/**
 * Finds all elements in the entire page matching `selector`, even if they are in shadowRoots.
 * Just like `querySelectorAll`, but automatically expand on all child `shadowRoot` elements.
 * @see https://stackoverflow.com/a/71692555/2228771
 */
function querySelectorAllShadows(selector, el = document.body) {
  // recurse on childShadows
  const childShadows = Array.from(el.querySelectorAll('*')).
    map(el => el.shadowRoot).filter(Boolean);

  // console.log('[querySelectorAllShadows]', selector, el, `(${childShadows.length} shadowRoots)`);

  const childResults = childShadows.map(child => querySelectorAllShadows(selector, child));
  
  // fuse all results into singular, flat array
  const result = Array.from(el.querySelectorAll(selector));
  return result.concat(childResults).flat();
}
// examples:
querySelectorAllShadows('td'); // all `td`s in body
querySelectorAllShadows('.btn') // all `.btn`s in body
querySelectorAllShadows('a', document.querySelector('#right-nav')); // all `a`s in right menu
Domi
  • 18,735
  • 12
  • 77
  • 106