什麼是Shadow DOM?

什麼是Shadow DOM?

幾周前,我寫了一篇關於到底是什麼DOM的文章。回顧一下,文檔對象模型是HTML文檔的表示。瀏覽器使用它來肯定頁面上要呈現的內容,並經過Javascript程序來修改頁面的內容,結構或樣式。php

例如,讓咱們採用如下HTML文檔:html

<!doctype html>
<html lang="en">
 <head>
   <title>My first web page</title>
  </head>
 <body>
    <h1>Hello, world!</h1>
    <p>How are you?</p>
  </body>
</html>

上面的HTML文檔將產生如下DOM樹。web

  • html瀏覽器

    • headapp

      • titledom

        • My first web page
    • bodysvg

      • h1工具

        • Hello, world!
      • p組件化

        • How are you?

在過去幾年中,您可能據說過「Shadow DOM」和「Virtual DOM」等術語。這些雖然固然與原始DOM有關,但它們指的是大相徑庭的概念。在本文中,我將詳細介紹Shadow DOM以及它與原始DOM的區別。在之後的文章中,我將對虛擬DOM進行分析。url

一切都是 global 的👍🏾!等等,一切都是 global 的👎🏾

HTML文檔中的全部元素和樣式以及DOM都位於一個全局範圍內。頁面上的任何元素均可以經過document.querySelector()方法訪問,不管它在文檔中的嵌套程度如何或放置在何處。一樣,應用於文檔的CSS能夠選擇任何元素,不管它在何處。

當咱們想要將樣式應用於整個文檔時,能夠直接使用*選擇頁面上的每一個元素並將它們的盒模型進行修改。box-sizing

* { box-sizing: border-box }

另外一方面,有些時候元素須要徹底封裝,咱們不但願它受到全局樣式的影響。一個很好的例子是第三方小部件,例如Twitter的「關注」按鈕。如下是該小部件的示例:

Follow @ireaderinokun
clipboard.png

Twitter follow @ireaderinokun

假設您啓用了Javascript而且檢查了元素,您會注意到該按鈕是一個<iframe>元素,您實際看到的樣式按鈕實際上是加載一個小文檔。

clipboard.png

這是Twitter能夠確保其小部件的預期樣式不受文檔中的任何CSS影響的惟一方式。儘管有一些方法可使用級聯來嘗試實現相同的結果,可是沒有其餘方法能夠提供與<iframe>相同的效果。

建立Shadow DOM是爲了容許在Web平臺上本地封裝和組件化,而沒必要依賴像<iframe>這樣的工具,而這些工具實際上並非爲此目的而製做的。

DOM中的DOM

您能夠將shadow DOM視爲「DOM中的DOM」。它是本身獨立的DOM樹,具備本身的元素和樣式,與原始DOM徹底隔離。

雖然最近才指定供Web做者使用,但用戶代理多年來一直使用shadow DOM來建立和設置複雜組件(如表單元素)。咱們來看一下input。要在頁面上建立一個,咱們所要作的就是添加如下元素:

<input type="range">

若是咱們深刻挖掘,咱們會看到這個<input>元素其實是由幾個較小的<div>元素組成,控制着軌道和滑塊自己。

clipboard.png

這是使用shadow DOM實現的。暴露給主機HTML的元素記錄了簡單的<input>,但在其下面有與組件相關的元素和樣式,它們不構成DOM全局​​範圍的一部分。

shadow DOM如何工做

爲了說明shadow DOM的工做原理,讓咱們使用shadow DOM而不是<iframe>來從新建立Twitter「follow」按鈕。

首先,咱們從shadow host開始。這是咱們想要將新影子DOM附加到原始DOM中的常規HTML元素。對於像Follow按鈕這樣的組件,它還能夠包含咱們但願在頁面上未啓用Javascript或不支持shadow DOM時顯示的回退元素。

<span class="shadow-host">
  <a href="https://twitter.com/ireaderinokun">
     Follow @ireaderinokun
  </a>
</span>

請注意,咱們不只僅使用<a>元素做爲影子主機,由於某些元素(主要是交互元素)不能是影子主機。

要將陰影DOM附加到咱們的主機,咱們使用attachShadow()方法。

const shadowEl = document.querySelector(".shadow-host");
const shadow = shadowEl.attachShadow({mode: 'open'});

這將建立一個空的shadow root做爲咱們的shadow host的子項。shadow root 是新的shadow DOM的開始,其方式是<html>元素是原始DOM的開頭。咱們能夠經過#shadow-root 在devtools檢查器中看到咱們的shadow-root。

clipboard.png

雖然常規HTML子項在檢查器中是可見的,可是當shadow root接管時,它們在頁面上再也不可見。

接下來,咱們要建立內容以造成新的shadow tree。shadow tree就像一個DOM樹,可是對於shadow DOM而不是常規DOM。要建立咱們的跟隨按鈕,咱們所須要的只是一個新的<a>元素,它與咱們已有的後備連接幾乎徹底相同,但帶有一個圖標。

const link = document.createElement("a");
link.href = shadowEl.querySelector("a").href;
link.innerHTML = 
    <span aria-label="Twitter icon"></span> 
    ${shadowEl.querySelector("a").textContent}
;

咱們將這個新元素添加到咱們的shadow DOM中,就像使用appendChild()方法將任何元素做爲子元素添加到另外一個元素同樣。

shadow.appendChild(link);

在這一點上,這是咱們的元素的樣子:

https://p0.ssl.qhimg.com/t012...
clipboard.png

最後,咱們能夠經過建立一個<style>元素並將其附加到陰影根來添加一些樣式。

const styles = document.createElement("style");
styles.textContent = 
a, span {
  vertical-align: top;
  display: inline-block;
  box-sizing: border-box;
}

a {
    height: 20px;
    padding: 1px 8px 1px 6px;
    background-color: #1b95e0;
    color: #fff;
    border-radius: 3px;
    font-weight: 500;
    font-size: 11px;
    font-family:'Helvetica Neue', Arial, sans-serif;
    line-height: 18px;
    text-decoration: none;   
}

a:hover {  background-color: #0c7abf; }

span {
    position: relative;
    top: 2px;
    width: 14px;
    height: 14px;
    margin-right: 3px;
    background: transparent 0 0 no-repeat;
    background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);
}
;

shadow.appendChild(styles);

這是咱們的最後一個要素:

clipboard.png

DOM與shadow DOM
在某些方面,shadow DOM是DOM的「精簡」版本。與DOM同樣,它是HTML元素的表示,用於肯定在頁面上呈現的內容並啓用元素的修改。但與DOM不一樣,shadow DOM不是基於完整的獨立文檔。正如其名稱所示,shadow DOM始終附加到常規DOM中的元素。沒有DOM,shadow DOM就不存在了。

Share the Article on Twitter

Share the Article on Facebook

Post the Article to Reddit

相關文章
相關標籤/搜索