In our last post, we used execute_script() to navigate Shadow DOMs in ServiceNow, which required writing JavaScript to traverse into shadow roots. This method worked but was complex and hard to maintain.
A Better Way: Directly Accessing Shadow DOMs with shadow_root
Selenium’s Python shadow_root function now provides a simpler alternative. Here’s how to approach this in a step-by-step guide.
Step 1: Old Approach with JavaScript
Let’s first recall the old approach. We needed to execute JavaScript to access the shadow DOM, as shown:
 
## To click a shadow element
driver.execute_script("return <js_path>.click()")
This method allowed interaction but added complexity due to manually executing JavaScript to achieve the interactions we want.
Step 2: The New Approach with shadow_root
With the recent improvements in Selenium, we can now directly access shadow DOMs without executing JavaScript. Here’s how it works:
# 1. Locate the Shadow Host # First, identify the shadow host element using its CSS selector: shadow_host = driver.find_element(By.CSS_SELECTOR, "shadow-host-selector") # 2. Access the Shadow Root shadow_root = shadow_host.shadow_root # 3. Find the Element within the Shadow DOM # With the shadow root exposed, # you can now interact with elements inside it using regular CSS selectors: element_in_shadow = shadow_root.find_element(By.CSS_SELECTOR, "inner-element-selector")
No need for complex JavaScript paths—just clean, readable code.
Note: When accessing elements within the shadow DOM using shadow_root, only CSS_SELECTOR can be used to find elements. Other selectors like XPath are not supported.
Example: Interacting with ServiceNow All Tab
 
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from servicenow_selenium.servicenow_selenium import ServiceNowSelenium
# Setup your ServiceNow instance credentials
url = "https://seamlessmigrationllcdemo2.service-now.com/"
username = "midserver_selenium"
password = "testPassword2112$"
# Initialize ServiceNow Selenium
snTest = ServiceNowSelenium(url, username, password)
driver = snTest.driver
# Log in
try:
    snTest.login()
    time.sleep(5)  # Let the login process complete
except Exception as e:
    print(f"Login failed with error: {e}")
    driver.quit()
    exit()
# Traverse shadow hosts & shadow roots until "All" is reached
try:
    # Wait for the element to be present in the DOM
    shadow_host1 = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.XPATH, "//*[starts-with(name(), 'macroponent')]"))
    )
    shadow_root1 = shadow_host1.shadow_root
    # Continue accessing the subsequent shadow hosts and roots
    shadow_host2 = shadow_root1.find_element(By.CSS_SELECTOR, 'sn-polaris-layout')
    shadow_root2 = shadow_host2.shadow_root
    shadow_host3 = shadow_root2.find_element(By.CSS_SELECTOR, 'sn-polaris-header')
    shadow_root3 = shadow_host3.shadow_root
    # Find the 'All' menu button and click it
    all_menu_button = shadow_root3.find_element(By.CSS_SELECTOR, 'div[aria-label="All"]')
    all_menu_button.click()
    time.sleep(15) ## To view the all menu clicked
except Exception as e:
    print(f"An error occurred: {e}")
# Logout and quit
snTest.logout_endpoint()
driver.quit()
In this example, we started from the first shadow host, “macroponent”, and traversed our way down the Shadow DOM until we reached the desired element. From there, then end result is us able to click the “All” tab in the Servicenow navbar menu.
Difference Between ShadowRoot and WebElement
When accessing a shadow DOM with shadow_root, it’s important to note that the object returned is a ShadowRoot, which is distinct from the traditional WebElement object. Within a ShadowRoot, you can only use the following methods:
- find_element
- find_elements
- session
Only CSS_SELECTOR is allowed as the selector inside a ShadowRoot, and no other types of selectors (like XPath) are supported.
The elements retrieved from within the ShadowRoot are regular WebElement objects, allowing you to interact with them using familiar methods such as click(), get_attribute(), and more.
Key Takeaways
- No More JavaScript Execution: Selenium’s shadow_rootsimplifies interaction with Shadow DOM elements by allowing direct access through CSS selectors.
- Cleaner Code: This approach results in more readable, maintainable test scripts.
- More Efficient Testing: By removing the need for JavaScript execution, test scripts run faster and are easier to debug.
- Limited Functions in ShadowRoot: ShadowRootonly supportsfind_element,find_elements, andsession, unlikeWebElement, which provides a wide range of interaction methods.
- Selector Limitation: Only CSS_SELECTORis allowed withinShadowRoot, no other selectors (like XPath) work.
 
								