Access Shadow Root Elements
Overview
The shadow_root_elements
function locates elements within one or more nested Shadow DOM (shadow root) levels on a web page. It is used when target elements are encapsulated inside Web Components or browser-internal pages that utilize Shadow DOM, where standard document queries (CSS or XPath) cannot access them directly.
Internally, the function sequentially finds each shadow host and then accesses its shadow DOM, for example, using Selenium's shadow_root
method or a JavaScript call such as driver.execute_script('return arguments[0].shadowRoot', host)
.
This feature should be used when the required element is hidden within an open-mode shadow root and must be accessed through its host elements. Note that Element.shadowRoot
only returns the shadow DOM if the root is open; therefore, this function cannot access closed shadow roots.
This capability enables traversal through nested Shadow DOMs to locate target elements, which is essential for modern web applications that rely on encapsulation using Shadow DOM.
Key Concepts
- Shadow Host: Element that contains a Shadow Root.
- Shadow Root: An encapsulated subtree attached to a Shadow Host.
- Parent Scope: An optional container element for Shadow Hosts, specified using the
parent
parameter.
User Input Structure
Users define Shadow DOM traversal using step data by following these rules:
1. Shadow Root Parameters
- Format:
sr <index> <type> parameter
- index: An integer (e.g., 1, 2, 3.....) representing the nesting level. If omitted, it defaults to
sr 1
. - type: Use
element parameter
to specify a Shadow Host orparent parameter
to define the container scope.
- index: An integer (e.g., 1, 2, 3.....) representing the nesting level. If omitted, it defaults to
- Allowed Attributes:
id
,class
,tag
and any other attribute (e.g.,title
,data-qa
).- Text selectors are not supported; use attribute-based selectors instead.
Example:
["id", "sr 1 parent parameter", "container"]
["tag", "sr 1 element parameter", "user-profile"]
2. Target Element Parameters
Define the final element within the deepest Shadow Root using the standard element parameter
syntax, using only CSS selectors.
Example:
["class", "element parameter", "submit-button"]
Traversal Workflow
-
Process Parameters
- Group the
sr
parameters based on theirindex
andtype
(eitherelement
orparent
). - Validate that indices are unique and follow a sequential order.
- Group the
-
For Each Shadow Level
- Parent Scope (if provided)
- Locate the parent container using a CSS selector.
- Search for the Shadow Host within the specified parent.
- Shadow Host
- Construct a CSS selector using element attributes.
- Locate the element within the current root or parent scope.
- Enter Shadow Root
- Access it using
element.shadowRoot
. - Fail the operation if no Shadow Root exists.
- Access it using
- Parent Scope (if provided)
-
Locate Target Element
- After traversing all Shadow Roots, proceed with the operation:
- Construct the CSS selector for the final element.
- Return the element(s) from the deepest Shadow Root.
- After traversing all Shadow Roots, proceed with the operation:
Rules and Restrictions
-
Selector Types
- Shadow Hosts: Use CSS selectors only; XPath and text-based selectors are not supported.
- Target Elements: Except for the first level, use CSS selectors only; before entering the Shadow DOM, there are no restrictions on selector type.
-
Index Handling
- If the index is omitted,
sr 1
is assumed implicitly. - Consecutive indices are required (e.g.,
sr 1
must be followed bysr 2
). - A parent is not required; one level may contain a parent, while another level may not.
- If the index is omitted,
-
Parent Scoping
- The parent must exist within the current root, either the main document or an active Shadow Root.
- The Shadow Host is searched within the specified parent element.
-
Error Cases
- Shadow Host was not found.
- The element has no Shadow Root.
- The index or parameter format is invalid.
Example Use Case
HTML:
<div id="app"> <!-- Parent for 1st Shadow Host -->
<user-card> <!-- Shadow Host (Level 1) -->
#shadow-root
<!-- Inside user-card's Shadow Root -->
<div class="profile"> <!-- Shadow Host (Level 2) -->
#shadow-root
<button class="save">Save</button> <!-- Target -->
</div>
</user-card>
</div>
Step Data:
shadow_root_ds = [
["id", "sr 1 parent parameter", "app"], # Parent of 1st Shadow Host
["tag", "sr 1 element parameter", "user-card"], # Shadow Host (Level 1)
["class", "sr 2 element parameter", "profile"] # Shadow Host (Level 2)
]
element_ds = [
["class", "element parameter", "save"] # Target element
["click", "selenium action", "click"]
]
The user must provide all of this data in a single action.
Traversal:
- Find the parent element
#app
, then locate the<user-card>
element within it. - Enter the Shadow Root of the
<user-card>
element. - Locate the
.profile
element and enter its Shadow Root. - Locate the
.save
button. - Click the Save button.
Page DOM (main document)
└─ Shadow Host (sr-1) [e.g. <custom-element id="outer-host">]
└─ Shadow DOM Level 1
└─ Shadow Host (sr-2) [e.g. <inner-element class="inner">]
└─ Shadow DOM Level 2
└─ [Target Element, e.g. <button>]
Why are only CSS selectors used?
- Shadow DOM encapsulation prevents XPath from traversing its internal structure.
- CSS selectors pierce Shadow Boundaries via
::shadow
or/deep/
deprecation. - Ensures consistency and compatibility across browsers.
Troubleshooting Tips
- Shadow Host not found
- Verify that the parent scope exists within the current root.
- Check for typos in attribute names and values.
- No Shadow Root:
- Ensure the element is a Shadow Host (verify using DevTools).
- Use the
allow-hidden
option if the element is not yet rendered.
- Index Mismatches:
- Explicitly specify indices (e.g., use
sr 2
instead of justsr
).
- Explicitly specify indices (e.g., use