Skip to main content

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​

Diego Molina
Elemental Selenium maintainer