javascript - 从 Shadow DOM 中访问元素

标签 javascript web-component shadow-dom documentfragment

我创建了一个 Web 组件,并希望访问元素 来自 组件。
我正在使用 .attachMode({mode:'closed'}),因此父级无权访问。

<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 和 document 都引用父窗口。而且没有人可以访问影子根。
  • 尝试将 shadowroot 存储在全局变量中,并从片段或 connectedCallback 内部访问它。
    即使这样可行,它也会破坏使用 {mode:'closed'} 的意义,但无论如何它都不起作用。

  • 我有一个有效的黑客,但无法想象我必须使用它。
    封装的全部意义在于事物可以是自包含的,但是如果 JS 不能作用于其容器中的其他项目,那对我们有什么好处呢?

    如果这是解决方案,希望有一个提示来解释组件实现方式的逻辑。
    不过,这是 hack:包含一个运行 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>
    

    关于类似问题( 250483591663305755101967 等)的注意事项 - 当模式关闭时,这些答案将不起作用,这是我需要的。

    最佳答案

    看起来您在元素引用和 this 中都有错误范围(在 <script> 中)

    const shadowRoot = this.attachShadow({mode: 'closed'}).appendChild(clone);
    

    appendChild 是你的克星。

    它返回插入的元素... 不是 一个影子根,但一个 #document-fragment (克隆模板)

    固定为:
     const shadowRoot = this.attachShadow({mode: 'closed'});
     shadowRoot.appendChild(clone);
    

    然后:
  • mode:closed不是 分配 this.shadowRoot ..
    你可以不是 重复使用,因为它仍然是只读属性

  • 固定为:
     this.Root = this.attachShadow({mode: 'closed'});
     this.Root.appendChild(clone);
    

    您现在可以执行以下操作:
      connectedCallback() {
        this.Root.querySelector('#outside').innerHTML = 'Changed';
      }
    

    我不明白你为什么认为这不理想this.Root可通过所有/任何方法访问 里面 组件

    所有 DOM 方法的一个很好的资源是:https://javascript.info/modifying-document
    <script><template> 内(这个范围)

    <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>
    

    您将模板注入(inject)到组件中document可以不是 访问元素 里面 任何组件 shadowDOM
    shadowDOM 是否为 mode:closed 无关紧要或 mode:open<script> 范围 将是 window (因为它没有被分配一个范围)

    (我认为您不能为 SCRIPT 设置 this 范围)

    获取 <script> 内的组件范围你必须有创意..
    并使用您的img onload= “黑客”

    onload<style> element 让它少了一点黑客攻击(恕我直言)

    <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行为不端:
  • onload<STYLE>元素只会为 触发第一个元素
    我使用 <img> 更改了 JSFiddle 以使用您的第一个 hack , 它为每个元素触发
  • templFunc()在 shadowDOM 中加载/作用域,因此可以从组件方法调用(参见 JSFiddle)
    但是.. 仅在 FireFox 未定义/不可用第一个元素
    现在我认为这是一个 FireFox 错误...将进一步调查...(大胆去哪里...)

  • !!!更新 #2 !!!

    哎呀!用它玩了一些。

    从克隆和导入的 SCRIPT 中找出所有变量和函数
    被吊到全局window范围

    所以上面的代码有效,但有一个主要的副作用......

    这也是FireFox提示的原因。回复 声明 let变量

    关于javascript - 从 Shadow DOM 中访问元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60097743/

    相关文章:

    javascript - 即使使用 Bostock 的换行功能,文本也不会换行在 d3 SVG 元素中

    javascript - Ionic 下拉选定的问题

    Javascript - 如何按顺序从数组中选取随机元素?

    javascript - 使用 babel 转译基于类的 Web 组件

    javascript - 我们如何检测影子根是使用 v0 还是 v1 API 创建的?

    html - 如何将 Polymer 与现有样式表一起使用?

    javascript - 选中任何复选框时如何更改 img 类?

    javascript - 从自身访问 Web 组件元素

    reactjs - 将 React 渲染到 Shadow Root 中

    javascript - 是否可以保留自定义元素的内部 html?