Shadow DOM系列5-JavaScript

英文链接:Shadow DOM: JavaScript, 02 SEPTEMBER 2013 on Web Components, Shadow DOM

我们目前已经对模板HTML引入和 Shadow DOM(简介基础样式样式续) 有了一定了解。所有这些技术的终极目标就是自定义元素(custom elements),但是我们尚未企及这一终点。在你迈向这一终点之前,我还希望你能理解使用 JavaScript 操作 Shadow DOM 的基本方式。所以在本文中我将要阐述一些需要注意的问题——特别是关于事件(events)是如何工作的。当你掌握了这些知识,你就能更好的创建你的自定义元素啦。

让我们开整吧!

在今天开始之前,我想要感谢 Eric Bidelman 的这篇介绍 Shadow DOM 样式添加的宏文(可以戳中文译版)。本文的大部分都是我对他这篇博文内容的实践。如果有机会的话你一定要去读一下HTLM5 Rocks 关于 Web Components 的全部文章

技术支持

我建议你使用 Chrome v33+ 来实验本文的例子,因为 33+ 的 Chrome 对我所描述的这些新特性都有浏览器的原生支持。

JavaScript 作用域

还记得之前我花了大把时间解释 Shadow DOM 的 CSS 是怎么封装并被保护不遭受原始文档的侵袭的,这种方式是不是很碉堡?你可能会猜 JavaScript 也是用这种碉堡的方式工作的,我一开始也这么想的,但是事实上完全不是我们想的那样。除了我们稍后会讨论的一些例外情况,Shadow DOM 里的 JavaScript 的使用方式和之前其实区别不大。这就意味着往年你所学习的那些 JavaScript 最佳实践仍然可以在 Shadow DOM 里使用。

这里就是一个使用 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>

enter image description here

尽管我们使用了模板标签且 <script> 代码块写在 Shadow DOM,变量 foo 仍然挂载到了window 上。这里没有什么特殊的魔法将变量移出全局作用域。相反我们需要依赖 IIFE(立即执行函数表达式,使用函数作用域避免全局变量污染,JS 里著名的 trick,译者注),这位可靠的朋友确保一切都得到保障。

<template>  
  <h1>Hello World!</h1>
  
  <script>
  (function () {
    var foo = 'bar';
  }());
  </script>
</template>

enter image description here

这样才像那么回事儿么!

事件重定向(Event Retargeting)

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 事件。

被阻塞的事件(Blocked Events)

有些情况下事件绑定不进行重定向而直接被干掉。以下时间会被阻塞到根节点且不会被原有 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 支持的浏览器上实现这些特性的。

转载于:https://my.oschina.net/1pei/blog/490851

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值