querySelector that can pierce Shadow DOM roots without knowing the path through nested shadow roots. Useful for automated testing of Web Components e.g. with Selenium, Puppeteer.
// available as an ES6 module for importing in Browser environments
import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';
- querySelectorAllDeep - mirrors
querySelectorAll
from the browser, will return anArray
of elements matching the query - querySelectorDeep - mirrors
querySelector
from the browser, will return thefirst
matching element of the query.
Both of the methods above accept a 2nd parameter, see section Provide alternative node
. This will change the starting element to search from i.e. it will find ancestors of that node that match the query.
Playwright works really nicely with this package.
This module exposes a playwright selectorEngine
: https://github.com/microsoft/playwright/blob/master/docs/api.md#selectorsregisterenginefunction-args
const { selectorEngine } = require("query-selector-shadow-dom/plugins/playwright");
const playwright = require('playwright');
...
await playwright.selectors.register(selectorEngine, { name: 'shadow' })
...
await page.goto('chrome://downloads');
// shadow= allows a css query selector that automatically pierces shadow roots.
await page.waitForSelector('shadow=#no-downloads span', {timeout: 3000})
For a full example see: https://github.com/Georgegriff/query-selector-shadow-dom/blob/master/examples/playwright
There are some puppeteer examples available in the examples folder of this repository.
// query from another node
querySelectorShadowDom.querySelectorAllDeep('child', document.querySelector('#startNode'));
// query an iframe
querySelectorShadowDom.querySelectorAllDeep('child', iframe.contentDocument);
This library does not allow you to query across iframe boundaries, you will need to get a reference to the iframe you want to interact with.
If your iframe is inside of a shadow root you could cuse querySelectorDeep
to find the iframe, then pass the contentDocument
into the 2nd argument of querySelectorDeep
or querySelectorAllDeep
.
In the below examples the components being searched for are nested within web components shadowRoots
.
// Download and Paste the lib code in dist into chrome://downloads console to try it out :)
console.log(querySelectorShadowDom.querySelectorAllDeep('downloads-item:nth-child(4) #remove'));
console.log(querySelectorShadowDom.querySelectorAllDeep('#downloads-list .is-active a[href^="https://"]'));
console.log(querySelectorShadowDom.querySelectorDeep('#downloads-list div#title-area + a'));
If using the polyfills and shady DOM, this library will still work.
- Shipped as an ES6 module to be included using a bundler of your choice (or not).
- ES5 version bundled ontop the window as
window.querySelectorShadowDom
available for easy include into a test framework
npm install
npm test
npm run watch
npm run build