20

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
  • 29,002
  • 9
  • 92
  • 134
Yaroslav Polubedov
  • 1,352
  • 2
  • 11
  • 13

3 Answers3

34

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.

3rd party edit 2022

The following might help to illustrate the question. Give there is only 1 <input type=range> in the html document this code shows if its children can be accessed.

 // returns 1 as expected since only one input element is in the document
document.querySelectorAll("input").length; 

// get a reference to <input type=range>
var rangeInput = document.querySelector("input");     

// Is it a shadowRoot?
// if null then either 
//   - it is not a shadowRoot OR 
//   - its elements can not be accessed (mode == closed)
console.log(rangeInput.shadowRoot);  // returns null

The code above shows that the internals of an <input type=range> can not be accessed.

input_type_range_shadowRoot_access

surfmuggle
  • 5,527
  • 7
  • 48
  • 77
Supersharp
  • 29,002
  • 9
  • 92
  • 134
  • 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
17

To answer a generalized version of the OP's question:

Query shadow elements FROM ANYWHERE on the page?

It feels like the shadow root API is still lacking. It seems to make querySelectorAll useless, in that querySelectorAll will not actually get all matching elements anymore, since it ignores all descendants in shadowRoots. Maybe there is an API that fixes that, but since I have not found any, I wrote my own:

This function 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
  • 22,151
  • 15
  • 92
  • 122
  • That's not really an omission, its by design, and its the main mechanism by which shadowDOM 'encapsulates' styles and namespace. querySelect/+all is a method of the capital D Document interface of which lowercase d document is the most famous member, but Shadowroots are also documents (or document fragments, really). Documents within documents within documents... its documents all the way down man – diopside May 11 '23 at 04:13
11

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
  • 10,292
  • 2
  • 24
  • 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
  • 2
    This doesn't seem to work in recent Firefox: `"message": "TypeError: container.createShadowRoot is not a function"` – Jolta Mar 12 '19 at 10:20
  • 3
    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
  • 4
    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
  • 1
    `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