How to Find Elements in the Shadow DOM
Intro
Many pages are built with web components (e.g., design systems, Salesforce Lightning, Ionic). A web component keeps its elements in a shadow root: a small DOM tree attached to the component (the shadow host), apart from the rest of the page. Its styles and its elements don't leak out, and the page's CSS and scripts don't leak in.
That includes our locators: #inner-button finds nothing when the button is in a shadow root, even when it's right
there on the screen. And a component can contain other components, each with its own shadow root.
A Solution
Go through the shadow roots, one at a time:
- Selenium gets the shadow root of an element (
shadow_root,getShadowRoot()), and finds elements inside it with a CSS selector, like in the page. To reach a nested component, repeat it: host, shadow root, host, shadow root, element. - Selenide has
shadowCss, which takes the element and the shadow hosts on the way to it. - Playwright and WebdriverIO look inside open shadow roots on their own, so the usual locator works.
WebdriverIO also has
shadow$, to go one shadow root at a time.
This works for open shadow roots, the most common kind. Closed shadow roots (mode: 'closed') hide their elements
from the page's scripts too, and only some tools reach them.
Vibium is not part of this tip: its find doesn't look inside shadow roots yet.
Example
The example uses the advanced shadow DOM page of
the-internet. The outer-card component has a shadow root, which contains the inner-card component, which has a
shadow root with a "Click me" button (#inner-button). Clicking it changes the text of #nested-result, in the
page.
One test clicks the button and checks the text. The other one shows that a normal locator (or, in Playwright, XPath) doesn't find the button.
Expected Behavior
- The browser starts and loads the page
- The test finds the button inside the two shadow roots and clicks it
- The page shows
You clicked the button in the inner shadow root, and the test checks it - A normal locator (Selenium) or XPath (Playwright) doesn't find the button
- The browser closes
Summary
An element in a shadow root needs a way into the shadow root. With Selenium, get the shadow root of each host on the way, and use CSS selectors inside it (XPath doesn't work there). With Playwright and WebdriverIO, the usual CSS locators look inside on their own.
Happy Testing!
Authors
