如何在 shadow DOM 中选择节点?考虑以下示例:
“无阴影”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>
在像“OK: querySelect()”这样的评论中,我展示了我试图从任何阴影 DOM 外部运行的选择器。
我已经阅读了以下文章:http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/?redirect_from_locale=ru 并根据文章中所说的事实,查询像:JS 中的
document.querySelector('app-element::shadow h2');
应该按预期工作。但是在 Dart 中它不起作用。
我做错了什么?
伪选择器
::shadow
和组合器 /deep/
在 Firefox 上不起作用。
使用
.shadowRoot
var shadowroot = app-element.shadowRoot;
shadowroot.querySelector('h2');
Update2(来自评论)
如果您使用自定义 main,请确保在尝试与 Polymer 元素交互之前正确初始化 Polymer(有关更多详细信息,请参阅如何在 Polymer 应用程序中实现 main 函数)。
我通常建议避免自定义 main 并创建一个
app-element
(或您喜欢的任何名称)并将您的初始化代码放入 attached
(确保调用 super.attached();
)或 ready()
(不需要超级电话)。
原创
在这种情况下,它似乎不在影子 DOM 中,而是在孩子中。
这应该有效:
querySelector('h2');
只有当它在您的元素中时它才会在影子 DOM 中
<template>...</template>
当您将它包装在自定义元素的标签中时它不会出现。
<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>
更新
如果你想搜索
在当前元素的影子 DOM 中
shadowRoot.querySelect('h2');
在影子 DOM 内的元素的影子 DOM 内
shadowRoot.querySelector('* /deep/ h2');
shadowRoot.querySelector('ui-button::shadow h2');
来自当前元素之外
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');
对于想要易于使用的解决方案的人
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
}
像这样使用它:
var nodes = $$$('#some .selector')
// use from a custom rootNode
var buttonsWithinFirstNode = $$$('button', nodes[0])
它会遍历根节点内的所有元素,所以速度不会很快,但很容易使用。
使用 reduce 方法的纯香草辅助函数
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);
}
并像这样使用它
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);
这是一个老问题,但令我惊讶的是没有通用的解决方案(不仅仅是在 Dart 中)。让我们为所有情况解决它!
// 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;
}
尝试示例:
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.';
注意:这也可以递归解决,但我只是用迭代来解决。
上述解决方案对我不起作用,但这确实有效:
function $$(selector, element = document.body) {
if (element.shadowRoot) {
element = element.shadowRoot;
const found = element.querySelector(selector);
if (found) return found;
}
for (let i = 0; i < element.children.length; i++) {
const child = element.children[i];
const found = $$(selector, child);
if (found) {
return found;
}
}
return null;
}
像
$$('.my-text-box')
一样使用