Recommended Free Tools
To automate an element inside an open Shadow DOM, first locate its shadow host and enter its shadow root in Selenium; Playwright locators can cross open shadow roots automatically. XPath is the important exception in Playwright: it does not pierce shadow roots. Closed roots are not available for direct traversal, so test the component through its public behavior or an agreed test hook.
What Shadow DOM changes for browser automation
Shadow DOM attaches a separate DOM tree to an ordinary element. The ordinary element is the shadow host; its internal tree is the shadow tree; the dividing line is the shadow boundary; and the entry point is the shadow root. This encapsulation prevents page code and styles from freely reaching into component internals. See MDN’s Shadow DOM guide and the W3C Shadow DOM specification.
That boundary changes how selectors work. A document-level query usually cannot find a button nested inside a shadow tree. Automation must either enter the root explicitly, as with Selenium, or use a framework locator that supports crossing open roots, as Playwright does. The component’s root mode also matters: open roots can be traversed; closed roots intentionally withhold ordinary direct access.
Choose the right approach: Selenium or Playwright
| Question | Selenium | Playwright |
|---|---|---|
| How do I reach an open root? | Locate the host, then access its shadow_root (Python) or call GetShadowRoot() (.NET). |
Supported locators pierce open shadow roots automatically. |
| Can I use XPath? | After entering a ShadowRoot, use strategies supported by the language binding and root search context. | No. XPath does not pierce shadow roots. |
| Can I directly traverse a closed root? | No, not through the closed boundary. | No. Closed-mode roots are unsupported. |
| What locators should I prefer? | A stable host selector and a focused descendant selector. | Roles, accessible names, visible text, or an explicit test ID rather than long structural chains. |
These behaviors are documented in the Selenium element-finding documentation and Playwright locator guidance. Check the relevant framework documentation when upgrading dependencies.
#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
Automate a shadow element with Selenium
Selenium’s Python API uses an explicit host-to-root sequence. Wait for the host to be present and, where the component has a readiness signal, for its content to be ready. Then find descendants from the returned root rather than from the page document.
- Wait for and locate the custom-element host with a selector that is stable for your application.
- Read the host’s
shadow_root. - Find the target from that root and perform the action.
- Assert a visible or otherwise observable result, not merely that the click call returned.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Assumes driver has already navigated to the page.
host = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "my-component"))
)
root = host.shadow_root
button = root.find_element(By.CSS_SELECTOR, "button.submit")
button.click()
# Replace this with an assertion for the application's observable result.
confirmation = WebDriverWait(driver, 10).until(
lambda d: root.find_element(By.CSS_SELECTOR, ".status").text == "Saved"
)
assert confirmation
The selector and status text above are examples; substitute the actual host, target, and expected outcome. Selenium’s documentation illustrates the same shadow_host then shadow_root pattern and notes that nested lookup can require two browser commands. Where a suitable single locator can avoid an extra command, that may reduce the lookup round trip; retain the explicit root step when it makes the boundary and target clearer.
For nested shadow roots
If the target sits inside a component nested within another component’s root, traverse one host at a time: locate the outer host, get its root, locate the inner host from that root, get the inner root, then find the target. Do not assume a document-level selector can jump across several boundaries.
Rank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
outer_host = driver.find_element(By.CSS_SELECTOR, "outer-panel")
outer_root = outer_host.shadow_root
inner_host = outer_root.find_element(By.CSS_SELECTOR, "inner-form")
inner_root = inner_host.shadow_root
submit = inner_root.find_element(By.CSS_SELECTOR, "button[type='submit']")
submit.click()
Automate an open shadow root with Playwright
For Playwright, use a locator that describes what a user sees whenever possible. A role and accessible name are usually more resilient than a chain of internal classes; visible text is also appropriate when it uniquely identifies the target. Playwright automatically pierces open shadow roots for supported locators.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
import { test, expect } from '@playwright/test';
test('submits the form in an open web component', async ({ page }) => {
await page.goto('https://example.com');
await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByText('Saved')).toBeVisible();
});
Replace the URL and labels with those in your application. The example assumes the button is exposed with the accessible role and name shown. If your team has established a test-ID contract, configure and use that test ID instead. Avoid long CSS and XPath chains tied to private component structure: they can break when the implementation changes without any user-visible behavior changing.
Why XPath fails in Playwright
Playwright’s XPath locators do not cross a shadow boundary. If an XPath works for ordinary DOM but returns no match for a component’s internal element, switch to a role, text, configured test ID, or supported CSS locator that can pierce the open root. Changing timeouts will not make XPath cross the boundary.
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
Open and closed roots: what automation can access
A component created with attachShadow({mode: 'open'}) exposes its root through the host’s shadowRoot property to page JavaScript. With mode: 'closed', that property does not provide the root for ordinary direct traversal. This is an encapsulation choice, not a selector syntax problem; adding more CSS or XPath cannot cross it.
For a closed-root component, test the contract available to consumers: interact with the component as presented, verify its accessible role and name or emitted outcome, or arrange a test-only hook with the component author. Do not make tests depend on private internals that the component deliberately hides.
Reliability checklist
- Confirm whether the component uses an open or closed root.
- Wait for the host and its relevant content to be ready before querying descendants; presence of the host alone does not guarantee that asynchronously rendered content is available.
- Prefer accessible roles, names, visible text, and explicit test IDs over selectors that mirror internal markup.
- Put repeated ShadowRoot traversal in a small helper so a component change is localized.
- Assert the user-visible result or public behavior after an action.
- Recheck the automation framework’s documentation when upgrading browser automation dependencies.
Troubleshooting common failures
“No such element” or locator timeout
Check that the host selector matches the element on the current page, then verify that the host has rendered its root and target. Add an appropriate wait for the host or component state. In Selenium, search for the descendant from the ShadowRoot, not from driver; in Playwright, use a supported locator and confirm that the root is open.
Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
The host is found, but the target is not
The target may be in a nested shadow root, may render only after an interaction, or may have changed. Inspect the component structure and traverse each nested host-to-root boundary explicitly in Selenium. In Playwright, check that the target is actually inside an open root and that the locator matches its user-facing role, name, or text.
XPath finds ordinary elements but not the component’s internals
In Playwright this is expected: XPath does not pierce shadow roots. Replace it with a supported locator such as role, text, test ID, or CSS. In Selenium, enter the ShadowRoot first and search from that root using a supported strategy.
Direct access works in one component but not another
The components may use different root modes. An open root can be accessed through supported traversal; a closed root cannot be directly inspected through its boundary. Use the component’s public contract or coordinate a test hook rather than trying increasingly complex selectors.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Click succeeds but the test still fails
A successful click only establishes that the automation issued an action. Wait for and assert the resulting UI state, navigation, or application outcome. Also check whether the component requires a prior state change before the target is actionable.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
For a rendered page screenshot rather than an interactive browser test, ScreenshotNeo provides a screenshot API and MCP server. A single GET request captures a URL as an image or PDF; it does not replace Selenium or Playwright for clicking and asserting component behavior. Its capture can accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers indicate the page verdict and billing status. An MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots.
Example using cURL (replace the URL with the page you want to capture):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for setup and options. Other supported examples:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo is at screenshotneo.com. Sign up free for 1,000 screenshots a month with no card.
Frequently Asked Questions
Does browser automation change or bypass a closed shadow root?
No. A closed root withholds ordinary direct traversal; use the component’s public behavior or an agreed test hook.
Should I use CSS selectors or accessibility locators for shadow content?
Prefer user-facing roles, accessible names, and text, or an explicit test ID contract. Use structural selectors only when needed and keep them focused.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

