英文连接:Shadow DOM: JavaScript, 02 SEPTEMBER 2013 on Web Components, Shadow DOMjavascript
咱们目前已经对模板、HTML引入和 Shadow DOM(简介、基础、样式、样式续) 有了必定了解。全部这些技术的终极目标就是自定义元素(custom elements),可是咱们还没有企及这一终点。在你迈向这一终点以前,我还但愿你能理解使用 JavaScript 操做 Shadow DOM 的基本方式。因此在本文中我将要阐述一些须要注意的问题——特别是关于事件(events)是如何工做的。当你掌握了这些知识,你就能更好的建立你的自定义元素啦。html
让咱们开整吧!html5
在今天开始以前,我想要感谢 Eric Bidelman 的这篇介绍 Shadow DOM 样式添加的宏文(能够戳中文译版)。本文的大部分都是我对他这篇博文内容的实践。若是有机会的话你必定要去读一下HTLM5 Rocks 关于 Web Components 的所有文章。java
我建议你使用 Chrome v33+ 来实验本文的例子,由于 33+ 的 Chrome 对我所描述的这些新特性都有浏览器的原生支持。web
还记得以前我花了大把时间解释 Shadow DOM 的 CSS 是怎么封装并被保护不遭受原始文档的侵袭的,这种方式是否是很碉堡?你可能会猜 JavaScript 也是用这种碉堡的方式工做的,我一开始也这么想的,可是事实上彻底不是咱们想的那样。除了咱们稍后会讨论的一些例外状况,Shadow DOM 里的 JavaScript 的使用方式和以前其实区别不大。这就意味着往年你所学习的那些 JavaScript 最佳实践仍然能够在 Shadow DOM 里使用。express
这里就是一个使用 JS 的例子:浏览器
<body> <div id="host"></div> <template> <h1>Hello World!</h1> <script> var foo = 'bar'; </script> </template> <script> var host = document.querySelector('#host'); var root = host.createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); console.log('window.foo = ' + window.foo); </script> </body>
尽管咱们使用了模板标签且 <script>
代码块写在 Shadow DOM,变量 foo
仍然挂载到了window
上。这里没有什么特殊的魔法将变量移出全局做用域。相反咱们须要依赖 IIFE(当即执行函数表达式,使用函数做用域避免全局变量污染,JS 里著名的 trick,译者注),这位可靠的朋友确保一切都获得保障。app
<template> <h1>Hello World!</h1> <script> (function () { var foo = 'bar'; }()); </script> </template>
这样才像那么回事儿么!dom
Shadow DOM 里的 JS 与传统的 JS 一个真正不一样的点在于事件调度(event dispatching)。要记住的一点是:原来绑定在 shadow DOM 节点中的事件被重定向了,因此他们看起来像绑定在影子宿主上同样。分布式
我知道没有个例子的话大家都不知道我在说啥……
<body> <input id="normal-text" type="text" value="I'm normal text"> <div id="host"></div> <template> <input id="shadow-text" type="text" value="I'm shadow text"> </template> <script> var host = document.querySelector('#host'); var root = host.createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); document.addEventListener('click', function(e) { console.log(e.target.id + ' clicked!'); }); </script> </body>
能够戳上面这个例子的 JS Bin 连接。
分别点击上面两个文本输入框并查看控制台的输出。当你点击“normal text”的输入框时控制台输出这个输入框的 id
,然而当你点击“shadow text”的输入框时控制台却输出了宿主元素(就是#host
)的 id
。这是由于影子节点上的事件必须重定向,不然这将破坏封装性。若是时间帮顶继续指向 #shadow-text
那么任何人均可以在咱们的 Shadow DOM 里乱搞而破坏咱们内部的结构。
若是你能回想起上一篇博文咱们讨论的分布节点——也就是投射到 Shadow DOM 中的影子宿主中的内容。你可能会认为既然这些节点出如今 Shadow DOM 中,那么他们上面的时间应该也被重定向了。但事实并不是如此。
这有另一个例子来证实以上的观点。
<body> <input id="normal-text" type="text" value="I'm normal text"> <div id="host"> <input id="distributed-text" type="text" value="I'm distributed text"> </div> <template> <div> <input id="shadow-text" type="text" value="I'm shadow text"> </div> <div> <content></content> </div> </template> <script> var host = document.querySelector('#host'); var root = host.createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); document.addEventListener('click', function(e) { console.log(e.target.id + ' clicked!'); }); </script> </body>
能够戳上面这个例子的 JS Bin 连接。
和刚才同样,当你点击每个输入框的时候你会看到绑定了事件的元素的 id
。在“dustributed text”上点击你会发现他的事件绑定元素是未被重定向的。这是由于分布节点来自原有 DOM 结构,而用户是能够操做原有 DOM 结构的。对它进行事件重定向没啥必要,并且事实上你也不想让它重定向。若是一个使用者给你提供一个按钮来对 Shadow DOM 设置样式,有时候他们可能会但愿监听它的 click
事件。
有些状况下事件绑定不进行重定向而直接被干掉。如下时间会被阻塞到根节点且不会被原有 DOM 结构监听到:
abort
error
select
change
load
reset
reset
resize
scroll
selectstart
举个例子说明一下:
<body> <input id="normal-text" type="text" value="I'm normal text"> <div id="host"> <input id="distributed-text" type="text" value="I'm distributed text"> </div> <template> <div> <content></content> </div> <div> <input id="shadow-text" type="text" value="I'm shadow text"> </div> </template> <script> var host = document.querySelector('#host'); var root = host.createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); document.addEventListener('select', function(e) { console.log(e.target.id + ' text selected!'); }); </script> </body>
能够戳上面这个例子的 JS Bin 连接。
这里我对 select
事件进行了监听,当你点击并拖动鼠标选中文本的时候就会触发这一事件。若是你尝试在“normal text(原有 DOM 结构)”的输入框中选中文本,控制台会输出 normal-text text selected!
。而你对做为分布节点的“distributed text”输入框进行操做也会获得类似的结果。可是若是你尝试选中做为影子节点的“shadow text”输入框,控制台什么都不会输出。选择的时间在影子根节点中被干掉了所以不能冒泡到文档中,而咱们的事件监听重定向至文档,所以没法监听到这一事件。若是你想在你的 Shadow DOM 中使用以上事件,请记住他们会被扼杀在根节点。
我但愿我讲的还行吧……尽管在 JavaScript 的使用上有一些小陷阱,可是大部分用法都和往常相同。若是你通读了以前的博文,那作好准备吧,自定义元素和 Polymer 在新世界等着你的到来!
译者注
webcomponents.js 在页面组件层面主要实现了三个内容:自定义元素、HTML 引入以及 Shadow DOM,对于模板和 HTML 引入的内容稍后会进行翻译。本系列的主要目的为了解 Shadow DOM 的原理并探究做为 polyfill 的 webcomponents 是如何在非 native 支持的浏览器上实现这些特性的。