能否通过父文档访问Shadow DOM元素?

40

这个问题更倾向于用户创建的影子 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元素?

你的问题是如何访问影子 DOM 中的 DOM 元素?另外,你的 JSFiddle 不完整。 - Menelaos
不,如何通过shadow DOM的父文档访问shadow DOM元素。<script>/*在此处访问*/</script><input type="date" <!-- shadow DOM --> />。不完整? - James Donnelly
4个回答

23
现在(2016年),您可以使用Shadow DOM根上的querySelector方法访问开放的用户创建的 Shadow DOM元素(但不能访问用户代理创建的Shadow DOM!)。请注意保留HTML标签。
<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>


为什么我们仍然无法访问用户代理 Shadow DOM? - Harvey Lin
我猜这是因为它是浏览器的本地实现,而不是真正的HTML。 - Supersharp
那么,没有办法获取这些输入编辑器中的文本吗? - Harvey Lin
createShadowRoot() 方法已被弃用,推荐使用 attachShadow() 方法。详情请参考 attachShadow() - georgeawg

23

正如@int32_t所说,影子DOM(Shadow DOM)的定义是将要隐藏在外部源码之外的DOM填充到一个节点中 (封装)。重点在于您作为组件作者可以选择哪些部分将被公开到外部CSS或JavaScript,哪些不会。

不幸的是,您无法创建一个公共的JavaScript接口来访问您的Shadow DOM,除非使用另一个名为自定义元素的最新规范。如果您选择这样做,只需向元素的原型添加自定义公共方法即可。从这些方法中,您可以访问您的Shadow DOM的内部信息(请参见此处的第三个示例)。

然而,您可以通过CSS暴露用于访问您的Shadow DOM内部信息的钩子,而无需使用自定义元素。有两种方法可以实现:

  1. 伪元素
  2. CSS变量

伪元素

Chrome和Firefox通过特殊的伪元素将其Shadow DOM的某些部分暴露给CSS。 这里是一个示例,其中添加了一个CSS规则,仅适用于日期字段的数字部分,通过使用Chrome提供的-webkit-datetime-edit伪元素实现。

这里是可用的WebKit伪元素的部分列表。您还可以在DevTools中启用Show Shadow DOM选项并查找名为pseudo的属性。

组件作者还可以创建自己的伪元素来暴露其Shadow DOM的部分内容(请参见第2个示例 此处)。

CSS变量

更好的方法是使用CSS变量,您可以在Chrome中启用启用实验性WebKit功能,然后查看此范例,该范例使用CSS变量向Shadow DOM通信,告诉它应该使用什么颜色作为其“主题”。


1
毫无疑问,没有一些技巧你就不能访问Shadow DOM中的Javascript。但是否有办法在其中运行Javascript呢? - Marcy Sutton
我并不是最适合回答关于原生Shadow DOM的问题,但当你使用自定义元素时,你肯定可以这样做。例如,可以查看Polymer - CletusW
2
上面提供的示例代码已更新,新的示例代码链接为:http://jsfiddle.net/r3httdhs/。 - Ally
我喜欢这个答案... 但是,对于揭示使用CSS变量的最后一部分,我有一个问题评论... 这假定用户在其作用域内的阴影DOM中提供了样式表。 如果他们没有呢? 例如,如果他们只是在不提供变量的情况下进行了某些样式设置。 没有CSS变量和/或伪元素,如何访问SD中的类?顺便说一句,您拥有的jsfiddle似乎已经过时了。 - Christian Matthew

8
是的,您只需要调用 .root 或 .shadowRoot。以下是一个示例:
document.getElementById('itemId').root 

您若不在父级dom元素上使用innerText或innerHTML,将无法获取影子DOM元素。

4

你不能从Shadow DOM外部的脚本访问Shadow DOM的内容。Shadow DOM的目的是封装。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接