从 Shadow 中访问元素 DOM
Access Element from within Shadow DOM
我创建了一个 Web 组件,并希望从组件 中访问元素 。
我正在使用 .attachMode({mode:'closed'}),所以 parent 没有访问权限。
<template id='hello-world-template'>
<span id='inside'>Unchanged,</span> <span id='outside'>Unchanged</span>
<script>
document.querySelector('#inside').innerHTML = 'Changed';
// Ideal, but does not work - no such element
</script>
</template>
<hello-world></hello-world>
<script>
customElements.define('hello-world',
class extends HTMLElement {
constructor() {
super();
var template = document.getElementById('hello-world-template')
var clone = template.content.cloneNode(true)
const shadowRoot = this.attachShadow({mode: 'closed'}).appendChild(clone);
}
connectedCallback() {
this.shadowRoot.querySelector('#outside').innerHTML = 'Changed';
// Not ideal, and also does not work - this.shadowRoot has no querySelector
}
});
</script>
一些尝试:
- 在文档片段中 - this、self、window 和文档都引用了 parent window。并且none可以访问影子根。
- 尝试将 shadowroot 存储在全局变量中并从片段或 connectedCallback 内部访问它。
即使这行得通,它也会破坏使用 {mode:'closed'} 的意义,但无论如何它都行不通。
我有一个有用的 hack,但无法想象我必须使用它。
封装的全部意义在于事物可以自包含,但如果 JS 不能作用于其容器中的其他项,那对我们有什么好处?
如果这是解决方案,希望得到一个提示来解释组件实现方式的逻辑。
不过,这里有技巧:包括一个运行 JS onload 的图像。
<template id='hello-world-template'>
<span id='inside'>Unchanged,</span> <span id='outside'>Unchanged</span>
<script>
function runner(img){
let doc = img.parentNode;
doc.querySelector('#inside').innerHTML = 'Changed';
}
</script>
<img src='data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' onload="runner(this)">
</template>
<hello-world></hello-world>
关于类似问题(25048359, 16633057, 55101967 等)的注意事项 - 当模式关闭时,这些答案将不起作用,而我需要。
看起来您在元素引用和 this
范围(在 <script>
中)中都有错误
const shadowRoot = this.attachShadow({mode: 'closed'}).appendChild(clone);
appendChild
是你的克星。
它 returns 插入的元素... 不是影子根而是 #document-fragment
(克隆模板)
固定为:
const shadowRoot = this.attachShadow({mode: 'closed'});
shadowRoot.appendChild(clone);
然后:
mode:closed
将 不 分配 this.shadowRoot
..
你可以 not 重复使用,因为它仍然是只读的 属性
固定为:
this.Root = this.attachShadow({mode: 'closed'});
this.Root.appendChild(clone);
您现在可以:
connectedCallback() {
this.Root.querySelector('#outside').innerHTML = 'Changed';
}
我不明白你为什么认为这不理想
this.Root
可通过 all/any 方法访问 在 组件
中
所有 DOM 方法的一个很好的资源是:https://javascript.info/modifying-document
<script>
inside a <template>
(this scope)
<template id='hello-world-template'>
<span id='inside'>Unchanged</span>
<script>
document.querySelector('#inside').innerHTML = 'Changed';
// Ideal, but does not work - no such element
</script>
</template>
您将模板注入到组件中
document
可以 不 访问元素 内部 任何组件阴影 DOM
阴影 DOM 是 mode:closed
还是 mode:open
无关紧要
<script>
范围将是window
(因为它没有分配范围)
(我认为您不能为脚本设置 this 范围)
要在 <script>
中获取组件范围,您必须要有创意..
并使用你的 img onload=
'hack'
在 <style>
元素上使用 onload
可以减少黑客攻击(恕我直言)
<template id='hello-world-template'>
<span id='inside'>Inside Unchanged,</span>
<script>
function templFunc() {
// this scope is the shadowRoot, not the component!
this.querySelector('#inside').innerHTML = 'Changed';
}
</script>
<style onload="templFunc.apply(this.getRootNode())">
#inside{
color:green;
}
</style>
</template>
一个主要问题:只会对第一个使用的 <hello-world>
元素执行!!
这不是我写的,
(进行中)JSFiddle 游乐场(还显示引用组件 方法 )位于:
https://jsfiddle.net/CustomElementsExamples/zpamx728/
更新 #1
Chromium (Edge/Chrome) 和 Opera 都很好,FireFox v72.0.2
不正常:
<STYLE>
元素上的 onload
只会触发 第一个元素
我将 JSFiddle 更改为使用 <img>
来使用你的第一个 hack,它会为每个元素触发
templFunc()
在 shadowDOM 中是 loaded/scoped,因此可以从组件方法调用(参见 JSFiddle)
但是..仅在 FireFox 中 第一个元素
不是 defined/available
现在我认为这是一个 FireFox 错误...将进一步调查...(大胆去哪里...)
!!!更新 #2 !!!
糟糕!玩了一些。
从克隆和导入的 SCRIPT 中生成所有变量和函数
被提升到全局 window
范围
所以上面的代码有效,但有一个主要的副作用...
这也是 FireFox 抱怨 re- 声明 let
变量的原因
我创建了一个 Web 组件,并希望从组件 中访问元素 。
我正在使用 .attachMode({mode:'closed'}),所以 parent 没有访问权限。
<template id='hello-world-template'>
<span id='inside'>Unchanged,</span> <span id='outside'>Unchanged</span>
<script>
document.querySelector('#inside').innerHTML = 'Changed';
// Ideal, but does not work - no such element
</script>
</template>
<hello-world></hello-world>
<script>
customElements.define('hello-world',
class extends HTMLElement {
constructor() {
super();
var template = document.getElementById('hello-world-template')
var clone = template.content.cloneNode(true)
const shadowRoot = this.attachShadow({mode: 'closed'}).appendChild(clone);
}
connectedCallback() {
this.shadowRoot.querySelector('#outside').innerHTML = 'Changed';
// Not ideal, and also does not work - this.shadowRoot has no querySelector
}
});
</script>
一些尝试:
- 在文档片段中 - this、self、window 和文档都引用了 parent window。并且none可以访问影子根。
- 尝试将 shadowroot 存储在全局变量中并从片段或 connectedCallback 内部访问它。
即使这行得通,它也会破坏使用 {mode:'closed'} 的意义,但无论如何它都行不通。
我有一个有用的 hack,但无法想象我必须使用它。
封装的全部意义在于事物可以自包含,但如果 JS 不能作用于其容器中的其他项,那对我们有什么好处?
如果这是解决方案,希望得到一个提示来解释组件实现方式的逻辑。
不过,这里有技巧:包括一个运行 JS onload 的图像。
<template id='hello-world-template'>
<span id='inside'>Unchanged,</span> <span id='outside'>Unchanged</span>
<script>
function runner(img){
let doc = img.parentNode;
doc.querySelector('#inside').innerHTML = 'Changed';
}
</script>
<img src='data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' onload="runner(this)">
</template>
<hello-world></hello-world>
关于类似问题(25048359, 16633057, 55101967 等)的注意事项 - 当模式关闭时,这些答案将不起作用,而我需要。
看起来您在元素引用和 this
范围(在 <script>
中)中都有错误
const shadowRoot = this.attachShadow({mode: 'closed'}).appendChild(clone);
appendChild
是你的克星。
它 returns 插入的元素... 不是影子根而是 #document-fragment
(克隆模板)
固定为:
const shadowRoot = this.attachShadow({mode: 'closed'});
shadowRoot.appendChild(clone);
然后:
mode:closed
将 不 分配this.shadowRoot
..
你可以 not 重复使用,因为它仍然是只读的 属性
固定为:
this.Root = this.attachShadow({mode: 'closed'});
this.Root.appendChild(clone);
您现在可以:
connectedCallback() {
this.Root.querySelector('#outside').innerHTML = 'Changed';
}
我不明白你为什么认为这不理想
this.Root
可通过 all/any 方法访问 在 组件
所有 DOM 方法的一个很好的资源是:https://javascript.info/modifying-document
<script>
inside a <template>
(this scope)
<template id='hello-world-template'>
<span id='inside'>Unchanged</span>
<script>
document.querySelector('#inside').innerHTML = 'Changed';
// Ideal, but does not work - no such element
</script>
</template>
您将模板注入到组件中
document
可以 不 访问元素 内部 任何组件阴影 DOM
阴影 DOM 是 mode:closed
还是 mode:open
<script>
范围将是window
(因为它没有分配范围)
(我认为您不能为脚本设置 this 范围)
要在 <script>
中获取组件范围,您必须要有创意..
并使用你的 img onload=
'hack'
在 <style>
元素上使用 onload
可以减少黑客攻击(恕我直言)
<template id='hello-world-template'>
<span id='inside'>Inside Unchanged,</span>
<script>
function templFunc() {
// this scope is the shadowRoot, not the component!
this.querySelector('#inside').innerHTML = 'Changed';
}
</script>
<style onload="templFunc.apply(this.getRootNode())">
#inside{
color:green;
}
</style>
</template>
一个主要问题:只会对第一个使用的 <hello-world>
元素执行!!
这不是我写的,
(进行中)JSFiddle 游乐场(还显示引用组件 方法 )位于:
https://jsfiddle.net/CustomElementsExamples/zpamx728/
更新 #1
Chromium (Edge/Chrome) 和 Opera 都很好,FireFox v72.0.2
不正常:
<STYLE>
元素上的onload
只会触发 第一个元素
我将 JSFiddle 更改为使用<img>
来使用你的第一个 hack,它会为每个元素触发templFunc()
在 shadowDOM 中是 loaded/scoped,因此可以从组件方法调用(参见 JSFiddle)
但是..仅在 FireFox 中 第一个元素
不是 defined/available 现在我认为这是一个 FireFox 错误...将进一步调查...(大胆去哪里...)
!!!更新 #2 !!!
糟糕!玩了一些。
从克隆和导入的 SCRIPT 中生成所有变量和函数
被提升到全局 window
范围
所以上面的代码有效,但有一个主要的副作用...
这也是 FireFox 抱怨 re- 声明 let
变量的原因