Shadow DOM

Shadow DOM refers to the ability of the browser to include a subtree of DOM elements into the rendering of a document, but not into the main document DOM tree. Shadow DOM enables encapsulation. With Shadow DOM, a component can have its own “shadow” DOM tree that cannot be accidentally accessed from the main document. This subtree may have local style rules and more.

Identify Shadow DOM

You can identify if a site uses Shadow DOM through a browser's developer tools. Let's look at an example.

To identify Shadow DOM:

  1. In Google Chrome, navigate to https://shop.polymer-project.org.
  2. Open Developer tools (press the shortcut keys Fn+F12).
  3. On the Elements tab, expand the <body> element and the first element inside the <body> element and notice the #shadow-root line.

The following images show the element tree rendered for the desktop web view and for the mobile view.

Work with Shadow DOM elements using RemoteWebDriver

Trying to find Shadow DOM elements using Selenium locators results in NoSuchElementException. To access these Shadow DOM elements, we need to use the JavascriptExecutor executeScript() function. If you look at the DOM structure, every element that includes Shadow DOM also has a shadowRoot property that describes the underlying elements.

The following methods help you interact with Shadow DOM elements. All parameters listed are mandatory.

A sample GitHub project is located at https://github.com/PerfectoMobileSA/ShadowDOMSampleThe Shadow DOM Utility methods are located here.

findElementShadowDOM

This method finds the first Shadow DOM element matching the CSS selector and returns it.

Copy

Example: findElementShadowDOM method

WebElement parentShadowElement = driver.findElement(By.cssSelector("[page='home']"));
Map<String, Object> params = new HashMap<>();
params.put("parentElement", parentShadowElement);
params.put("innerSelector", "div#tabContainer shop-tabs shop-tab:nth-child(1) a");
WebElement innerDOMElement1 = findElementShadowDOM(((RemoteWebDriver)driver), params);

Parameters

Name Type Description

parentElement

WebElement

The shadow-root web element object

innerSelector

String

The CSS selector of the Shadow DOM inner element to search for

findElementsShadowDOM

This method finds all Shadow DOM elements matching the CSS selector and returns the list.

Copy

Example: findElementShadowDOM method

WebElement parentShadowElement = driver.findElement(By.cssSelector("[page='home']"));
Map<String, Object> params = new HashMap<>();
params.put("parentElement", parentShadowElement);
params.put("innerSelector", "div#tabContainer shop-tabs shop-tab a");
List<WebElement> allMatchingLinks = findElementsShadowDOM(((RemoteWebDriver)driver), params);

Parameters

Name Type Description

parentElement

WebElement

The shadow-root web element object

innerSelector

String

The CSS selector of the Shadow DOM inner element to search for

clickElementShadowDOM

This method performs a click operation on the first matching Shadow DOM element of the specified CSS locator.

Copy

Example: clickElementShadowDOM method

WebElement parentShadowElement = driver.findElement(By.cssSelector("[page='home']"));
Map<String, Object> params = new HashMap<>();
params.put("parentElement", parentShadowElement);
params.put("innerSelector", "div#tabContainer shop-tabs shop-tab:nth-child(1) a");
clickElementShadowDOM(((RemoteWebDriver)driver), params);

Parameters

Name Type Description

parentElement

WebElement

The shadow-root web element object

innerSelector

String

The CSS selector of the shadow DOM inner element to search for

sendKeysShadowDOM

This method enters text on the first matching Shadow DOM element of the specified CSS locator.

Copy

Example: sendKeysShadowDOM method

WebElement parentShadowElement = driver.findElement(By.cssSelector("[page='home']"));
Map<String, Object> params = new HashMap<>();
params.put("parentElement", parentShadowElement);
params.put("innerSelector", "div#tabContainer input#id1");
params.put("characterSequence", "Text to enter");
sendKeysShadowDOM(((RemoteWebDriver)driver), params);

Parameters

Name Type Description

parentElement

WebElement

The shadow-root web element object

innerSelector

String

The CSS-selector of the Shadow DOM inner element to search for

characterSequence

String

The text to insert in the edit field

getTextShadowDOM

This method returns the text of the first matching Shadow DOM element of the specified CSS locator.

Copy

Example: getTextShadowDOM method

WebElement homeEleShadowParent = driver.findElement(By.cssSelector("[page='home']"));
Map<String, Object> params = new HashMap<>();
params.put("parentElement", homeEleShadowParent);
params.put("innerSelector", "div#tabContainer shop-tabs shop-tab:nth-child(1) a");
String elementText = getTextShadowDOM(((RemoteWebDriver)driver), params);

Parameters

Name Type Description

parentElement

WebElement

The shadow-root web element object

innerSelector

String

The CSS selector of the Shadow DOM inner element to search for

getAttributeShadowDOM

This method returns the value of the given attribute of the first matching Shadow DOM element of the specified CSS locator.

Copy

Example: getAttributeShadowDOM method

WebElement homeEleShadowParent = driver.findElement(By.cssSelector("[page='home']"));
Map<String, Object> params = new HashMap<>();
params.put("parentElement", homeEleShadowParent);
params.put("innerSelector", "div#tabContainer shop-tabs shop-tab:nth-child(1) a");
params.put("attribute", "href");

String linkURL = getAttributeShadowDOM(((RemoteWebDriver)driver), params);

Parameters

Name Type Description

parentElement

WebElement

The shadow-root web element object

innerSelector

String

The CSS selector of the Shadow DOM inner element to search for

attribute

String

The name of the attribute

Work with nested Shadow DOM elements

Working with nested shadow DOM elements is similar to working with single-level Shadow DOM elements:

  1. Get the WebElement of the shadow-root using findElementShadowDOM method, .
  2. Provide this root WebElement as a parentElement to its child Shadow DOM element.
  3. Proceed until you reach the final Shadow DOM element.
Copy

Example: Nested Shadow DOM element

//Find the parent Shadow DOM element
WebElement parentShadowEle = driver.findElement(By.xpath("//vt-virustotal-app"));
Map<String, Object> params = new HashMap<>();
params.put("parentElement", parentShadowEle);
params.put("innerSelector", "home-view.iron-selected");
WebElement innerDOMElement1 = findElementShadowDOM(driver, params);

Map<String, Object> params2 = new HashMap<>();
params2.put("parentElement", innerDOMElement1);
params2.put("innerSelector", "#urlSearchInput");
WebElement innerDOMElement2 = findElementShadowDOM(driver, params);

//Find until the final Shadow DOM element is found.
Map<String, Object> params3 = new HashMap<>();
params3.put("parentElement", innerDOMElement2);
params3.put("innerSelector", "input#input");
WebElement finalShadowDOMElement = findElementShadowDOM (driver, params);

Integrate with Perfecto Smart Reporting

To integrate Shadow DOM with Perfecto Smart Reporting, contact the Perfecto Professional Services team.

The following images show the sample TestShadowDOM report in the CI Dashboard view.