How can I select nodes within shadow DOM? Consider the following example:
structure of "unshadowed" DOM
<app-element>
#shadow-root
<h2></h2>
<content>
#outside shadow
<h2></h2>
</content>
<ui-button>
#shadow-root
<h2></h2>
</ui-button>
</app-element>
index.html
<body>
<app-element>
<!-- OK: querySelect('app-element').querySelect('h2') -->
<!-- OK: querySelect('app-element h2') -->
<!-- There is no problem to select it -->
<h2>app-element > content > h2</h2>
</app-element>
</body>
templates.html
<polymer-element name="ui-button" noscript>
<template>
<!-- FAIL: querySelect('app-element::shadow ui-button::shadow h2') -->
<h2>app-element > ui-button > h2</h2>
</template>
</polymer-element>
<polymer-element name="app-element" noscript>
<template>
<!-- FAIL: querySelect('app-element::shadow').querySelect('h2') -->
<!-- FAIL: querySelect('app-element::shadow h2') -->
<!-- FAIL: querySelect('app-element').shadowRoot.querySelect('h2') -->
<h2>app-element > h2</h2>
<content></content>
<ui-button></ui-button>
</template>
</polymer-element>
In comments like "OK: querySelect()" I show selectors I've tried to run from outside any shadowed DOM.
I've already read the following article: http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/?redirect_from_locale=ru and based on the fact that it was said in the article, query like: document.querySelector('app-element::shadow h2');
in JS should work as expected. However in Dart it doesn't work.
What do I wrong?
Pseudo selector ::shadow
and combinator /deep/
doesn't work on firefox.
Use .shadowRoot
var shadowroot = app-element.shadowRoot;
shadowroot.querySelector('h2');
Update2 (from comments)
If you use a custom main, ensure that Polymer is properly initialized before you try to interact with your Polymer elements (see how to implement a main function in polymer apps for more details).
I usually suggest to avoid a custom main and create an app-element
(or whatever name you prefer) and put your initialization code into attached
(ensure to call super.attached();
) or in ready()
(doesn't need the super call).
Original
It seems in this case it's not in the shadow DOM but a child.
This should work:
querySelector('h2');
It's only in the shadow DOM when it is within your elements <template>...</template>
not when you wrap it in the tag of your custom element.
<polymer-element name="some-element">
<template>
<!-- this becomes the shadow DOM -->
<content>
<!--
what gets captureD by the content element becomes a child or some-element
-->
</content>
</template>
</polymer-element>
<body>
<some-element>
<!-- these elements here are captured by the
content tag and become children of some-element -->
<div>some text</div>
</some-element>
</body>
Update
If you want to search
inside the shadow DOM of the current element
shadowRoot.querySelect('h2');
inside the shadow DOM of an element inside the shadow DOM
shadowRoot.querySelector('* /deep/ h2');
shadowRoot.querySelector('ui-button::shadow h2');
from outside the current element
import 'dart:html' as dom;
...
dom.querySelector('* /deep/ h2');
// or (only in the shadow DOM of <app-element>)
dom.querySelector('app-element::shadow h2');
dom.querySelector('app-element::shadow ui-button::shadow h2');
// or (arbitrary depth)
dom.querySelector('app-element /deep/ h2');
For people wanting an easy to use solution
function $$$(selector, rootNode=document.body) {
const arr = []
const traverser = node => {
// 1. decline all nodes that are not elements
if(node.nodeType !== Node.ELEMENT_NODE) {
return
}
// 2. add the node to the array, if it matches the selector
if(node.matches(selector)) {
arr.push(node)
}
// 3. loop through the children
const children = node.children
if (children.length) {
for(const child of children) {
traverser(child)
}
}
// 4. check for shadow DOM, and loop through it's children
const shadowRoot = node.shadowRoot
if (shadowRoot) {
const shadowChildren = shadowRoot.children
for(const shadowChild of shadowChildren) {
traverser(shadowChild)
}
}
}
traverser(rootNode)
return arr
}
Use it like this:
var nodes = $$$('#some .selector')
// use from a custom rootNode
var buttonsWithinFirstNode = $$$('button', nodes[0])
It will traverse all the elements within the rootNode, so it won't be fast but it is easy to use.
Vanilla only helper function using reduce method
function queryShadow([firstShadowSelector, ...restOfTheShadowSelectors], itemSelector) {
const reduceFunction = (currShadow, nextShadowSelector) => currShadow.shadowRoot.querySelector(nextShadowSelector);
const firstShadow = document.querySelector(firstShadowSelector);
const lastShadow = restOfTheShadowSelectors.reduce(reduceFunction,firstShadow);
return lastShadow && lastShadow.querySelector(itemSelector);
}
and use it like this
const shadowSelectorsArr = ['vt-virustotal-app','file-view', '#report', 'vt-ui-file-card', 'vt-ui-generic-card'];
const foundDomElem = queryShadow(shadowSelectorsArr, '.file-id');
console.log(foundDomElem && foundDomElem.innerText);
This is an old question but I was amazed that there is no general solution (not just in Dart). Let's solve it for all cases !
// query elements even deeply within shadow doms. e.g.:
// ts-app::shadow paper-textarea::shadow paper-input-container
function querySelectorDeep(selector, root = document) {
let currentRoot = root;
let partials = selector.split('::shadow');
let elems = currentRoot.querySelectorAll(partials[0]);
for (let i = 1; i < partials.length; i++) {
let partial = partials[i];
let elemsInside = [];
for (let j = 0; j < elems.length; j++) {
let shadow = elems[j].shadowRoot;
if (shadow) {
const matchesInShadow = shadow.querySelectorAll(partial);
elemsInside = elemsInside.concat([... matchesInShadow]);
}
}
elems = elemsInside;
}
return elems;
}
Example to try it out:
let sel = `ts-app::shadow
paper-textarea::shadow
paper-input-container
iron-autogrow-textarea::shadow
textarea`;
textarea = querySelectorDeep(sel)?.[0];
textarea.value = 'If you see this, selector worked.';
Note: This could be solved recursively as well, but I just went with iterative.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.