是否可以通过父文档访问 Shadow DOM 元素?

Posted

技术标签:

【中文标题】是否可以通过父文档访问 Shadow DOM 元素?【英文标题】:Is it possible to access Shadow DOM elements through the parent document? 【发布时间】:2013-05-14 00:56:39 【问题描述】:

这个问题更针对用户创建的影子 DOM 元素,但为了便于访问,我将使用 date 输入类型来回答这个问题:

例如,我的页面上有一个date 输入。编辑掉一些位后,这个(使用 Chrome)的影子 DOM 标记看起来像:

<input type="date">
    #document-fragment
        <div pseudo="-webkit-datetime-edit">
            <div pseudo="-webkit-datetime-edit-fields-wrapper">
                <span role="spinbutton">dd</span>
                <div pseudo="-webkit-datetime-edit-text">/</div>
                <span role="spinbutton">mm</span>
                <div pseudo="-webkit-datetime-edit-text">/</div>
                <span role="spinbutton">yyyy</span>
            </div>
        </div>
        <div></div>
        <div pseudo="-webkit-calendar-picker-indicator"></div>

date 输入关联的方法和属性似乎根本没有引用影子 DOM (JSFiddle),所以我想知道如何(如果有的话)访问这些影子 DOM 元素?

【问题讨论】:

您的问题是如何访问shadow DOM中的DOM元素?您的 JSFiddle 也不完整。 不,如何通过 shadow DOM 的父文档访问 shadow DOM 元素。 &lt;script&gt;/*Access here*/&lt;/script&gt;&lt;input type="date" &lt;!-- shadow DOM --&gt; /&gt;。不完整? 【参考方案1】:

@int32_t 是正确的,根据定义,Shadow DOM 是一种用您想要对外部源隐藏的 DOM 填充节点的方法 (Encapsulation)。关键是您作为组件作者可以准确选择哪些部分将暴露给 CSS 或 javascript 之外,哪些不暴露。

不幸的是,如果不使用另一个名为 Custom Elements 的前沿规范,就无法为 Shadow DOM 创建公共 JavaScript 接口。如果您选择这样做,它就像将自定义公共方法添加到元素的原型一样简单。通过这些,您可以访问 Shadow DOM 的内部(参见第三个示例 here)。

但是,您可以公开 CSS 的钩子以访问 Shadow DOM 的内部,而无需使用自定义元素。有两种方法可以做到这一点:

    伪元素 CSS 变量

伪元素

Chrome 和 Firefox 通过特殊的伪元素将其 Shadow DOM 的某些部分暴露给 CSS。 Here's 您的 date 输入示例,通过使用 Chrome 提供的 -webkit-datetime-edit 伪元素添加了仅适用于日期字段的数字部分的 CSS 规则。

Here's 可用 WebKit 伪元素的部分列表。您也可以在 DevTools 中启用 Show Shadow DOM 选项并查找名为 pseudo 的属性。

组件作者还可以创建自己的伪元素来公开其 Shadow DOM 的一部分(参见第二个示例 here)。

CSS 变量

更好的方法是使用 CSS 变量,您可以在 Chrome 中使用 Enable experimental WebKit features in about:flags 启用。然后查看this fiddle,它使用 CSS 变量与 Shadow DOM 进行通信,它应该为其“主题”使用什么颜色。

【讨论】:

如果没有一些技巧,您不应该能够从 Javascript 访问 Shadow DOM,这是有道理的。但是有没有办法在那里运行 Javascript? 就香草 Shadow DOM 而言,我不是最好的人选,但当您使用自定义元素时,您当然可以。例如,查看Polymer。 上面提供的一个更新的小提琴:jsfiddle.net/r3httdhs 我喜欢这个答案......但是,有一个关于最后一部分的问题评论揭示了使用 CSS 变量的答案或方向......这假设用户在他们的范围内提供了一个样式表影子王国。如果他们没有呢?例如,如果他们只是在不提供变量的情况下设置样式怎么办。如果没有 css 变量和/或伪元素,如何访问 SD 中的类?顺便说一句,你所拥有的 jsfiddle 似乎已经被弃用了【参考方案2】:

现在(2016 年)您可以使用 Shadow DOM 根目录上的 querySelector 方法访问打开的用户创建的 shadow DOM 元素(但没有用户代理创建的 shadow DOM!):

<body>
    <div id="container"></div>
    <script>
        //Shadow Root
        ̶v̶a̶r̶ ̶r̶o̶o̶t̶ ̶=̶ ̶c̶o̶n̶t̶a̶i̶n̶e̶r̶.̶c̶r̶e̶a̶t̶e̶S̶h̶a̶d̶o̶w̶R̶o̶o̶t̶(̶)̶
        //new syntax:
        var root = container.attachShadow(  mode: "open"  )

        //Inside element
        var span = document.createElement( "span" )
        span.textContent = "i'm inside the Shadow DOM"
        span.id = "inside"
        root.appendChild( span )

        //Access inside element
        console.log( container.shadowRoot.querySelector( "#inside" ) )
    </script>
</body>

//Shadow Root
var root = container.createShadowRoot()

//Inside element
var span = document.createElement( "span" )
span.textContent = "i'm inside the Shadow DOM"
span.id = "inside"
root.appendChild( span )

//Access inside element
function get() 

  alert( container.shadowRoot.querySelector( "#inside" ).id )
<!DOCTYPE html>
<html>
<head>
    <title></title>
	<meta charset="utf-8" />
</head>
<body>
	<div id="container"></div>
    <button onclick="get()">Get</button>
	<script>
	</script>
</body>
</html>

【讨论】:

为什么我们仍然不能访问 user-agent shadow dom? 我想这是因为它是本机浏览器实现,而不是真正的 html。 那么没有办法在那种输入编辑器中抓取文本? createShadowRoot() 方法已被弃用,取而代之的是 attachShadow()【参考方案3】:

是的。你只需要调用 .root 或 .shadowRoot 。这是一个例子,

document.getElementById('itemId').root 

如果没有父 dom 元素上的 innerText 或 innerHTML,您将无法获得 shadow dom 元素。

【讨论】:

【参考方案4】:

您无法从 Shadow DOM 之外的脚本访问 Shadow DOM 的内容。封装是 Shadow DOM 的目的。

【讨论】:

以上是关于是否可以通过父文档访问 Shadow DOM 元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 shadow dom 创建聚合物自定义元素,以便可以访问它的 shadowRoot?

shadow dom 隔离代码 封装

为原生 Shadow DOM 元素设置样式

需要帮助了解 Shadow DOM

shadow-dom浅析

Shadow DOM 是不是能够保护元素?