What Is A Shadow DOM
What Is A Shadow DOM
Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree
— this shadow DOM tree starts with a shadow root, underneath which can be attached to
any elements you want, in the same way as the normal DOM.
Shadow host: The regular DOM node that the shadow DOM is attached to.
Shadow tree: The DOM tree inside the shadow DOM.
Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
Shadow root: The root node of the shadow tree.
The above section is taken from the MDN. You can read more about shadow DOM here.
How to access the shadow DOM?
You can access the shadow DOM by running a regular JavaScript in the main page context if
its mode is open.
Let us take the following HTML code snippet:
<div>
<div id="shell">
#shadow-root (open)
<div id="avatar"></div>
</div>
<a href="./logout.html">Logout</a>
</div>
As you can see it has both a shadow DOM (avatar) as well as regular DOM
elements( Logout link).
To access the shadow DOM elements using JavaScript you first need to query the shadow
host element and then can access its shadowRoot property. Once you have access to
the shadowRoot, you can query the rest of the DOM like regular JavaScript.
Java Code:
Python Code:
host = driver.find_element_by_id("shell"))
shadowRoot = driver.execute_script("return arguments[0].shadowRoot", host)
shadowRoot.find_elemen_by.id("avatar")).click()
Java Code:
host = driver.find_element_by_id("shell"))
driver.execute_script("return arguments[0].shadowRoot.getElementById('avatar').click()", host)
Sometimes there is a more complex DOM structure where we have multiple shadow DOMs
nested inside each other. If you inspect your Chrome browser’s Downloads page
– chrome://downloads/ you’ll find the following DOM structure:
Nest
ed shadow DOMs
As you can see there are three levels of shadow DOMs nested inside each other. What if
you want to access the target element <div id="leftContent"> that is inside the third
shadow DOM?
Well, you can apply the same principals we learned so far in this tutorial – Write JavaScript
to first access the shadow host, then get the shadow DOM by accessing
the shadowRoot property on the host. Once you have access to the first shadow DOM you
can traverse it and try to access the root of the second shadow DOM and so on.
document.getElementsByTagName('downloads-manager')[0]
.shadowRoot
.getElementById('toolbar')
.shadowRoot
.getElementById('toolbar')
.shadowRoot
.getElementById('leftContent')
If we want to click on that element we can inject the complete JavaScript to the browser:
Java Code:
Python Code:
firstHost = driver.find_element_by_tag_name("downloads-manager")
driver.execute_script("return
arguments[0].shadowRoot.getElementById('toolbar').shadowRoot.getElementById('toolbar').shadowRoot.getElem
entById('leftContent').click()", host)
We can optimise the above code and write a helper method that can return the shadow
DOM for any shadow host:
Java Code:
Python Code:
def getShadowRoot(host):
shadowRoot = driver.executeScript("return arguments[0].shadowRoot", host)
return shadowRoot
We can use this helper method every time we need access to the shadow DOM:
Java Code:
Python Code:
Challenge
Can you apply the learnings of this tutorial and type some text in the search bar for
Chrome’s downloads page? If you inspect the search bar and observe carefully you’ll find
that it is inside the nested third shadow DOM. Please feel free to ask for help or post your
solution in the comments below.