Shadow DOM系列5-JavaScript

英文連接: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

JavaScript 做用域

還記得以前我花了大把時間解釋 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>

enter image description here

儘管咱們使用了模板標籤且 <script> 代碼塊寫在 Shadow DOM,變量 foo 仍然掛載到了window 上。這裏沒有什麼特殊的魔法將變量移出全局做用域。相反咱們須要依賴 IIFE(當即執行函數表達式,使用函數做用域避免全局變量污染,JS 裏著名的 trick,譯者注),這位可靠的朋友確保一切都獲得保障。app

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

enter image description here

這樣纔像那麼回事兒麼!dom

事件重定向(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 支持的瀏覽器上實現這些特性的。

相關文章
相關標籤/搜索