这份规范描述了一种把多个 DOM 树结合进入一个层级结构中,并且这些树可以在同一份文档中交互的方法。这使得 DOM 结构能够更好的组合。
所有的图表、示例、注意事项,都和明示的非正式性章节一样是非正式性的。其余的部分都是正式性的。
关键字“必须”、“禁止”、“要求”、“会”、“不会”、“应该”、“不应该”、“推荐”、“可以”和“可选的”等出现在文档的正式性部分中都应该遵循 [[!RFC2119]] 中的解释。但考虑到可读性,这些词汇在规范中并没有以大写形式出现。
为了帮助分层,且避免在规范的不同部分之间产生循环依赖,该文档由三个连贯的叙述内容组成:
某种意义上说,这三个部分分别相当于是 数学——推理环境、物理——对概念的逻辑推理、和机械——对实践应用的推理。
任何遵循该规范的用户代理必须根据概念模型决定的交互或状态,都会以算法的方式进行捕获。该算法被定义为等价处理的术语。这里的等价处理是强制约束的算法实现,要求相同的输入通过用户代理实现和规范算法的输出是完全一致的。
请移步至 Shadow DOM 101 阅读这份非正式性介绍。
任何元素都可以有一个包含 0 个或更多的结点树的关联有序列表。
如果一个结点树是一个元素的关联列表的成员,则称该元素宿主该结点树。
如果超过一个 shadow 树被宿主于相同的 shadow 宿主,则最近添加的 shadow 树被称为更年轻的 shadow 树同时最早添加的 shadow 树被称为更老的 shadow 树。
如果没有比给定的 shadow 树更老的 shadow 树,则该 shadow 树被称为最老的 shadow 树。
如果没有比给定的 shadow 树更年轻的 shadow 树,则该 shadow 树被称为最年轻的 shadow 树。
更老的 shadow 根是指更老的 shadow 树的根结点。
更年轻的 shadow 根是指更年轻的 shadow 树的根结点。
最老的 shadow 根是指最老的 shadow 树的根结点。
最年轻的 shadow 根是指最年轻的 shadow 树的根结点。
方便起见,shadow 根提供了其自己的DOM 树存取器方法集合。除了 shadow 根的后代,没有结点可以访问这些方法。
这里引入树中树的目的在于方便下面章节中的算法定义。
像结点树被定义为结点之间的一套关系集合一样,树中树同样定义了结点树之间的一套关系集合:
一个 shadow 树的结点的 ownerDocument
属性必须引用该 shadow 树的 shadow 宿主的文档。
Window
对象中被命名的属性 [[!HTML]] 必须 访问该文档树中的结点。
如图,这里有七个结点树,分别命名为 A、B、C、D、E 和 F。 结点树 C、D 和 E 被宿主在同一个参与在结点树 A 上的 shadow 宿主。 结点树 C 是最老的 shadow 树。结点树 E 是最年轻的 shadow 树。 改图还存在下列关系:
组合树是指由树中树里的多个结点树作为结点架构而成的一个结点树。详细的组合树结构算法会稍后被规范。
Shadow 插入点是会发布更老的 shadow 根的子节点插入点。满足下列所有条件的 shadow 元素就相当于一个 shadow 插入点:
分布算法必须用来判定一个树中树的分布结果且必须等价于以下处理步骤:
分布决议算法必须用来判定一个给定的结点树及其后代树的分布结果,且必须等价于以下处理步骤:
池占据算法必须用来占据一个给定结点的子结点,且必须且必须等价于以下处理步骤:
池分布算法必须用来把一个池中的结点分布到其 shadow 树中的内容插入点中,且必须等价于以下处理步骤:
如果没有结点被分布到一个内容插入点 CONTENT 中,则 CONTENT 的子结点会被分布到 CONTENT 中作为后备结点。
一个插入点的匹配标准是一个复合选择器 [[!SELECTORS4]] 的集合。这些复合选择器被约束在了只包含这些单一选择器的范围内:
组合树子节点算法必须用来判定一个组合树中结点的子结点,且必须等价于以下处理步骤:
对一个给定的树中树 TREE-OF-TREES 来说,由 TREE-OF-TREES 构造出的组合树必须等价于以下处理步骤:
当一个Event(事件)在shadow tree中被dispatched(分发)时, 事件的路径要么穿过shadow trees要么在shadow root被终止. 其中一个例外情况就是mutation events(突发事件). mutation event types(突发事件) 一定 不能在shadow tree中被分发.
following events(以下事件) 一定是通常会在node tree(节点树)的root(根) node(节点)被阻止:
abort
error
select
change
load
reset
resize
scroll
selectstart
event path calculation algorithm(事件路径算法)一定是被用来决定事件路径并且一定要通过下列步骤(相当于以下的步骤的过程):
假设我们现在下列一个树:
A
is a document(文档).
E
, J
, N
, Q
, S
和V
是 shadow roots.
I
, M
, P
, R
和 U
是 content insertion points(内容的插入点).
X
是一个 shadow insertion point(阴影插入点).
这颗tree of trees(树)拥有以下的7棵node trees(节点树), 一棵document tree(文档树)和 6棵 shadow trees:
A
, B
, C
和 D
这些节点在文档树中.
B
. 节点E
, F
, G
, H
和I
这些节点在阴影树2中.
H
. 节点 J
, K
, L
和 M
这些节点在阴影树3中.
K
. 节点 N
, O
和 P
这些节点在阴影树4中.
O
. 节点 Q
和 R
这些节点在阴影树5中.
F
. 节点 S
, T
和 U
这些节点在阴影树6中.
B
. 节点 V
, W
和 X
这些节点在阴影树7中.
shadow tree 7是比shadow tree 2更早生成的.
让我们假设这颗tree of trees(树)的 分布结果是:
C
destination insertion points(目标插入点)是 [I, M]
(C
是被重新分布的)
L
destination insertion points(目标插入点)是 [P, R]
(L
是被重新分布的)
G
destination insertion points(目标插入点)是 [U]
F
destination insertion points(目标插入点)是 [X]
在这种情况下, 如果一个事件在被分发到D
上, 这个事件路径是:
[D, C, I, M, L, P, R, Q, O, N, K, J, H, G, U, T, S, F, E, X, W, V, B, A]
记录event path calculation algorithm(事件路径的算法) 是被设计用来实现以下目标:
这意味着如果我们获取一个node tree(节点树)的焦点并且忽视掉其他所有的 node trees(节点树), 事件路径将似乎看上就好像事件发生在我们所获取焦点的node tree(节点树)上. 这是一个非常重要的方面从某种意义上来说进行托管的阴影树在包含node tree(节点树)shadow host其中的情况下对于事件路径不具有任何影响 只要这个事件没有在descendant trees(子树)上被阻止.
举个例子, 通过document tree(文档树) 1的视图, 事件路径看上去将会是[D, C, B, A]
.
通过shadow tree 2的视图, 事件路径看上去将会是[I, H, G, F, E]
.
这种相似情况同样会应用于 node trees(节点树).
这也是值得我们指出的一种情况如果我们从事件路径中排除全部insertion points(插入点)和shadow roots, 这种结果等价于包括所有的祖先节点在生成树中分发事件.
在事件路径穿过大多数节点树的情况下, 事件信息包括事件的目标是被调整过的为了维持encapsulation(封装性). 事件retargeting(重定向)是一个当事件在每一个有祖先的节点上被分发时去计算相关联的目标. 一个relative target(相关联的目标)是一个node(节点),此给定了父元素的节点能最准确代表当前被分发事件的目标当保持封装性的时.
retargeting algorithm(重定向算法)被用来决定相关联的元素, 它must(必须) equivalent(等价)于经过下列的步奏:
重定向过程必须发送在一个事件被分发之前.
关系目标
一些事件有一个relatedTarget(关联目标)
[[!DOM-Level-3-Events]]属性, 这个属性是一个不是事件目标的node(节点), 但是和事件有关系.
举个实例, 一个mouseover
事件的relatedTarget(关联目标)
可能拥有一个node(节点)来自于鼠标被移动到事件target(目标)
.在relatedTarget(相关联目标)
是一个shadow tree的情况下, 符合标准的UAs 必须不能把真是的值泄露出这颗树. 在relatedTarget(相关联目标)
和target(目标)
都属于相同shadow tree的一部分, 符合标准的UAs 必须 阻止在阴影根上的事件去避免出现在mouseover
和 mouseout
事件触发来自于于同一节点的错误.
因此, 如果一个事件有 relatedTarget(相关联目标)
, 它的值和事件分发的范围 必须被调整. 通常来说:
relatedTarget(相关联的目标)
必须改为它的祖先 (或者它本身) 作为一个节点在同一个shadow tree中target(目标)
和relatedTarget(相关联的目标)
是相同的.related target resolution algorithm(相关联目标分辨算法) 必须必须用来决定relatedTarget(相关联目标)
属性的值并且必须equivalent(等价)于经过下列步骤的处理:
相关目标分辨算法的返回结果不总为null. 如果发生这种情况, 你需要对本标准提出bug并反馈给我们.
相关目标重定向的过程必须必须发生在一个事件分发之前.
Touch(触摸)
target(目标)
[[!TOUCH-EVENTS]] 属性必须被调整就像一个relatedTarget(相关目标)
的一个事件. 每一个Touch(触摸)
target(目标)
在TouchList(触摸列表)
的返回结果来自于TouchEvent(触摸事件)
的touches()
, changedTouches()
和targetTouches()
的结果一定是经过related target resolution algorithm(相关目标解析算法), 指定一个NODE(节点)和Touch(触摸)
target(目标)
作为参数.
focus(得到焦点)
, DOMFocusIn
, blur(失去焦点)
, 和DOMFocusOut
事件 必须被认为是和带有relatedTarget(相关)
的事件是同一种方式的, 符合的node(节点)的相关联的目标是在target(目标)得到焦点时又失去焦点时使用失去焦点时的结果或者 node(节点)获得焦点, 同时又造成它失去焦点target(目标)
行为的节点作为相关联的目标.
在分发事件的时候:
Event(事件)
target(目标)
和 currentTarget(当前目标)
的属性 必须返回一个relative target(相关联的目标)给node(节点)当节点的事件监听invoked(被触发)MouseEvent(鼠标事件)
的relatedTarget(相关目标)
属性 必须返回一个调整后的相关目标MouseEvent(鼠标事件)
的offsetX
和 offsetY
属性 必须 返回一个相对定位于坐标系初始padding edge(边距边缘)的relative target(相关元素)的坐标Touch(触摸)
target(目标)
属性 必须 返回一个 调整后的相关目标relatedTarget(相关目标)
和target(目标)
都指向同一个节点, 节点本身的事件监听一定不能被调用. TouchEvent(触摸事件)
在这个规则上没有问题.Event(事件)
eventPhase(解析) 属性 必须 返回一个 AT_TARGET 如果是relative target(相关元素) 同node(节点)一样当事件监听被调用bubbles(冒泡)
的属性值是false, 必须经过以下步骤:
eventPhase(事件解析)
属性AT_TARGET
在事件分发完成之上, Event(事件)
对象 target(目标)
和currentTarget(当前目标)
一定 必须应该是最顶层的祖先relative target(相关目标). 只要一段script可能控制了Event
对象在事件分发的范围内的传递, 这个步骤需要去避免暴露在shadow trees上的 nodes(节点).
假设我们现在有一个媒体控件的用户实例, 通过下列这个树来描述它, 由document tree(文档树)和shadow trees两部分组成. 在这个例子中, 我们将假设选择器被允许穿过阴影的边界并且我们将使用这些选择器去定义找到这些elements(元素). 当然, 我们也将创建一个虚构的shadow-root
element(元素) 去划分阴影边界并且 表现出 shadow roots:
<div id="player"> <shadow-root id="player-shadow-root"> <div id="controls"> <button id="play-button">PLAY</button> <input type="range" id="timeline"> <shadow-root id="timeline-shadow-root"> <div id="slider-thumb" id="timeline-slider-thumb"></div> </shadow-root> </input> <div id="volume-slider-container"> <input type="range" id="volume-slider"> <shadow-root id="volume-shadow-root"> <div id="slider-thumb" id="volume-slider-thumb"></div> </shadow-root> </input> </div> </div> </shadow-root> </div>
让我们通过一个选择器the volume slider's thumb (#volume-slider-thumb
)去指出控件的位置, 由此 在这个节点上去触发 一个mouseover
事件. 对于这个事件, 让我们假装认为没有relatedTarget(相关联的目标)
.
每一个retargeting algorithm(重定向算法), 我们都需要设置以下的祖先和相关联的目标:
Ancestor | Relative Target |
---|---|
#player |
#player |
#player-shadow-root |
#volume-slider |
#controls |
#volume-slider |
#volume-slider-container |
#volume-slider |
#volume-slider |
#volume-slider |
#volume-shadow-root |
#volume-slider-thumb |
#volume-slider-thumb |
#volume-slider-thumb |
在我们分发 mouseover
这个事件之后 使用 它们新计算出来的相关联目标, 用户决定去通过拇指去移动设备的时间线的位置
(#timeline-slider-thumb
). 这次触发有一次mouseout
事件对于the volume slider thumb 和一次mouseover
事件 对于 the timeline thumb.
让我们看看the volume thumb's的relatedTarget(相关联目标)
的值是如何被 mouseout
事件所影响的. 对于这次事件, relatedTarget(相关联事件)
是the timeline thumb (#timeline-slider-thumb
). 每一次的related target resolution algorithm(关联目标解析算法), 我们都应该设置以下的祖先和调整关联的目标:
Ancestor | Relative Target | Adjusted related Target |
---|---|---|
#player |
#player |
#player |
#player-shadow-root |
#volume-slider |
#timeline |
#controls |
#volume-slider |
#timeline |
#volume-slider-container |
#volume-slider |
#timeline |
#volume-slider |
#volume-slider |
#timeline |
#volume-shadow-root |
#volume-slider-thumb |
#timeline |
#volume-slider-thumb |
#volume-slider-thumb |
#timeline |
节点, #player
, target(目标)
和 relatedTarget(关联目标)
同时都有相同的值 (#player
), 这意味着它们并没有分发 node(节点)和它们的祖先上分发事件.
选区 [[!EDITING]]([[!可编辑的区域]])是没有被定义的. 选区的开发和实现应该做到最好. 这是一个可能公认的错误的方法:
由于nodes(节点)在不同的node(节点)树上绝对不可能拥有相同的根(元素),它们可能绝对不会存在一个有效的DOM范围内(这个DOM范围包含了多数的node(节点)树).
因此选区 是可能存在于唯一一个 node(节点)树里, 因为选区被定义在一个单独的范围里. 通过方法window.getSelection()
返回的选区绝对不会返回一个在shadow树里的选区.
shadow根对象的getSelection()
方法返回的是当前选区是在当前(上下文)的shadow tree.
如果一个节点不加入一个生成树(composed tree), 此节点 必须从[[!CSS3UI]]渲染树的顺序中被忽略
对于引导获取焦点的时序, 此引导顺序的时序对于一个给定的shadow tree A 必须 被插入到其他node tree(节点树) 引导顺序中 , 规则规则如下:
auto
去决定它的位置.对于有指向性的引导焦点, 它取决于用户代理完整的 引导顺序 中 shadow trees 在 文本中 引导顺序.
为了坚持封装性, 激活的元素在文档 对象的 focus(焦点) API 属性中的值 必须被调整的. 为了防止当调整这个值的时候丢失信息, 每一个 shadow root(阴影的根) 必须 也拥有一个激活元素的
属性用来保存在shadow tree中获取焦点激活元素的属性值
contenteditable
属性的值一定不能通过shadow host(阴影宿主) 传播到它本身的 shadow trees上.
用户代理可以通过辅助技术遍历composed tree(生成树), 因此可以使完整的WAI-ARIA(可访问的富因特网应用程序) [[!WAI-ARIA]]语义在shadow trees中使用.
相对来说, 一个shadow tree可以看作是存在于部分文档和文档本身之间某处的一个文档碎片. 当阴影树被渲染的时候, 单个shadow tree 目的是维持它本身在文档的传统树中的标准. 于此同时, 由于阴影树是抽象封装, 阴影树不能影响文档树. 因此, 阴影树中的HTML元素 必须 表现为指定的 [[!HTML]]的行为在shadow trees中, 除了少数例外的情况
HTML元素一个子集合确定 行为表现为 无效, 或者不属于document tree(文档树). 在这种一致的document fragment(文档碎片)中的这种HTML元素如何表现. 这样 elements(元素)有:
剩下的其他HTML元素在shadow trees里 必须 表现为就像它们出现在document tree(文档树)中的表现.
每一个 规范, 针对内容的渲染地方,一些HTM元素不是用来渲染它们的内容或针对特殊的需求所设计的. 是为了标准化HTML元素在composed tree(生成树)中渲染时的不同行为的,在shadow tree被创建和被填充进去的实例化元素的时候,所有的HTML元素必须具有相同的阴影树.这取决于用户代理定义阴影树.当然, 所有符合标准的用户代理必须满足以下的要求:
HTML Element | Shadow Tree Requirements |
---|---|
img , iframe , embed , object , video , audio , canvas , map , input , textarea , progress , meter |
如果这些元素可能有回调的内容, 包含一个内容的插入点. 这个(内容的插入点的)匹配条件的值是一个通用的选择器紧紧当这些元素需要展示回调内容时.其它方面, 包含没有内容插入点 或者一个内容插入点什么都无法匹配. |
fieldset |
包含两个内容的插入点要符合下列匹配条件:
|
details |
包含两个内容的插入点要符合下列匹配条件:
|
剩下所有elements(元素) | 包含一个内容插入点并且有universal selector(普通选择符)作为匹配条件 |
ShadowRoot
对象ShadowRoot
对象代表此shadow root.
返回当前在此shadow tree的区域
当此方法被调用, 它必须返回此shadow tree的selection(区域).
返回一个element(元素)指定的坐标系.
最终, 它需要是CSSOM View Module[[!CSSOM-VIEW]]标准的一部分
当此方法被调用, 它必须返回下列步骤运行后的结果:
ShadowRoot
的实例, 那么抛出一个InvalidNodeTypeError(无效节点的错误)
错误.x
大于viewport(视窗)的宽度不包括已经被渲染出来的滚动条(如果有), 或者如果是y
大于viewport(视窗)的高度不包括已经被渲染出来的滚动条(如果有), 返回null.X
和Y
坐标, 这样的方式决定于是否通过打点测试表示当前在shadow tree得到焦点的element(元素)
当读取时, 这个属性必须返回在当前在shadow tree中得到焦点的element(元素)的属性或者为null
, 如果这个属性不是空的.
表示此shadow host(阴影宿主),此hosts(宿主)有此context object(上下文对象).
当读取时, 这个属性必须返回此shadow host(宿主),这个hosts(宿主)有此context object(上下文对象).
表示此older shadow root(不是最新的阴影根)所关联到此context object(上下文对象)
当读取时, 此属性必须返回一个equivalent(等价)于下列步骤运行后的结果:
对于HTML elements(HTML元素), UA-provided shadow trees 一定是不能被使用的.
表示ShadowRoot
的标记上下文.
当读取该属性时, 此属性必须返回在此context object(上下文对象)中通过HTML fragment serialization algorithm(HTML文档序列化算法)所处理过的结果作为 shadow host(阴影宿主)
当设置该属性时,必须经过以下步骤:
shadow host(阴影宿主)
表示此shadow root的样式列表.
当读取该属性事时, 此属性必须返回一个StyleSheetList(样式表)
序列包含此shadow root的样式表.
一个ShadowRoot
实例的 nodeType(节点类型)
属性 必须 返回 DOCUMENT_FRAGMENT_NODE
. 因此, 一个ShadowRoot
实例的nodeName(节点名)
属性必须返回"#document-fragment"
.
调用方法cloneNode()
方法去复制ShadowRoot
实例时 必须通常会抛出一个 DATA_CLONE_ERR
异常.
Element(元素)
接口的扩展ShadowRoot
对象实例ShadowRoot
对象到一个有序的阴影根列表上根关联上的此context object(上下文对象)作为最新的shadow rootShadowRoot
对象.NodeList(节点列表)
由在destination insertion points(目标插入点)中的插入点的context object(上下文对象)所组成的.content(内容)
元素的content(内容)
表示一个在shadow tree中的insertion point(插入点).
如果一个content(内容)
元素不符合insertion point(插入点)的条件,
select(选择)
, 设置一个comma-separated tokens(对于逗号分割的标识)NodeList(节点列表)
对象.shadow
元素shadow
元素表示在shadow tree中的一个shadow insertion point(阴影插入点).
如果一个shadow
元素不满足insertion point(插入点)的条件, 它必须就像HTMLUnknownElement(未知HTML元素)
一样的渲染行为.
NodeList(节点列表)
对象.Event(事件)的
接口表示此事件对象路径.
当获取该属性时, 此属性必须 创建和返回一个新的JavaScript数组对象, 此数组对象从context object(上下文对象)的事件路径中拷贝而来.
使用Array(数组)
作为在WebIDL中返回类型的path(路径)
属性.
WebIDL的bugs: Array
subclassing
和 class, not interface.
Bob被要求在把一个简单的链接列表变成一个消息控件, 消息控件能够连接到2个不同类别的新闻: 突发新闻和最新新闻. 目前新闻报道的文档组成看上去就像这样:
<ul class="stories"> <li><a href="//example.com/stories/1">A story</a></li> <li><a href="//example.com/stories/2">Another story</a></li> <li class="breaking"><a href="//example.com/stories/3">Also a story</a></li> <li><a href="//example.com/stories/4">Yet another story</a></li> <li><a href="//example.com/stories/4">Awesome story</a></li> <li class="breaking"><a href="//example.com/stories/5">Horrible story</a></li> </ul>
为了管理新闻报道, Bob决定使用shadow DOM. 这样做允许Bob保持整个标签的整洁, 拥有可以控制插入点的权会使得通过类名分类新闻的任务变的非常简单. 在得到一杯Green Eye后, 他制作出了如下shadow tree,ul
元素成为了宿主:
<div class="breaking"> <ul> <content select=".breaking"></content> <!-- insertion point for breaking news --> </ul> </div> <div class="other"> <ul> <content></content> <!-- insertion point for the rest of the news --> </ul> </div>
Bob接下来根据来自于设计师的要求给新建立的消息控件样式,然后把它加入到shadow tree模型中:
<style> div.breaking { color: Red; font-size: 20px; border: 1px dashed Purple; } div.other { padding: 2px 0 0 0; border: 1px solid Cyan; } </style>
他仔细考虑了下他的公司是否应该找一个新的设计师, Bob将模型转化成了代码:
function createStoryGroup(className, contentSelector) { var group = document.createElement('div'); group.className = className; // Empty string in select attribute or absence thereof work the same, so no need for special handling. group.innerHTML = '<ul><content select="' + contentSelector + '"></content></ul>'; return group; } function createStyle() { var style = document.createElement('style'); style.textContent = 'div.breaking { color: Red;font-size: 20px; border: 1px dashed Purple; }' + 'div.other { padding: 2px 0 0 0; border: 1px solid Cyan; }'; return style; } function makeShadowTree(storyList) { var root = storyList.createShadowRoot(); root.appendChild(createStyle()); root.appendChild(createStoryGroup('breaking', '.breaking')); root.appendChild(createStoryGroup('other', '')); } document.addEventListener('DOMContentLoaded', function() { [].forEach.call(document.querySelectorAll('ul.stories'), makeShadowTree); });
干得好, Bob! 在咖啡还有半杯的情况下, 工作完成了. 意识到自己很厉害, Bob将自己的经验通过Puyo Puyo这样的方式传授给大家.
几个月过去了.
红旗飘飘了! 在Bob一年一度的研讨会中, Alice 负责增加 另一个, 临时的盒子在新闻控件中, 其中有许多关于选举的新闻. Alice阅读学习了Bob's代码, 阅读了shadow DOM的标准并且实现了, 多亏shadow tree复合的支持, 她并没有修改Bob的代码. 就像平常一样, 她的解决方法优雅而简单, 适当的在Bob的代码上做了点巧妙的变化:
// TODO(alice): BEGIN -- DELETE THIS CODE AFTER ELECTIONS ARE OVER. var ELECTION_BOX_REMOVAL_DEADLINE = ...; function createElectionStyle() { var style = document.createElement('style'); // TODO(alice): Check designer's desk for hallucinogens. style.textContent = 'div.election { color: Magenta; font-size: 24px; border: 2px dotted Fuchsia; }'; return style; } function makeElectionShadowTree(storyList) { var root = storyList.createShadowRoot(); // Add and style election story box. root.appendChild(createElectionStyle()); root.appendChild(createStoryGroup('election', '.election')); // Insert Bob's shadow tree under the election story box. root.appendChild(document.createElement('shadow')); } if (Date.now() < ELECTION_BOX_REMOVAL_DEADLINE) { document.addEventListener('DOMContentLoaded', function() { [].forEach.call(document.querySelectorAll('ul.stories'), makeElectionShadowTree); }); } // TODO(alice): END -- DELETE THIS CODE AFTER ELECTIONS ARE OVER.
使用shadow
元素允许Alice去组合Bob's组件到自己的组件里—在没有改变产品任何一行代码的情况下. 她对自己笑了, Alice意识到Bob已经想出一个方式关于保持文档标签整洁的想法, 但她是一个如此早的使用shadow tree组成这种很酷的方式去解决问题的人.
David Hyatt 开发了 XBL 1.0, 并且和Ian Hickson合写了XBL 2.0. 这两篇文档提供了极好的见解在函数闭包的问题并对本规范产生了巨大的影响.
Alex Russell和他非常有远见的想法引发了在shadow Dom这个主题上引发了狂热的浪潮和如何在web中实际应用起来.
Dominic Cooney, Hajime Morrita, and Roland Steiner不辞辛劳的工作在web平台范围里去解决函数闭包问题并且为这篇文档提供了稳固的基础.
编者也感谢像Alex Komoroske, Anne van Kesteren, Brandon Payton, Brian Kardell, Darin Fisher, Eric Bidelman, Deepak Sherveghar, Edward O'Connor, Elisée Maurer, Elliott Sprehn, Erik Arvidsson, Glenn Adams, Jonas Sicking, Malte Ubl, Mike Taylor, Oliver Nightingale, Olli Pettay, Rafael Weinstein, Richard Bradshaw, Ruud Steltenpool, Sam Dutton, Sergey G. Grekhov, Shinya Kawanaka, Tab Atkins, Takashi Sakamoto, and Yoshinori Sano,感谢他们对此标准的意见和协助.
此标准仍不完善. 还有许多工作需要做. 请通过阅读帮助我们并且记录bugs—千万不要忘记叫编者将你的名字添加到这里来.