Shadow DOM系列3-樣式

原文連接:Shadow DOM: Styles, 28 AUGUST 2013 on Web Components, Shadow DOMcss

昨天的博文的內容全是關於你的第一個 Shadow DOM 元素的組織和編碼。但我知道一大批圍觀羣衆都會問:咱們咋加樣式啊?!html

要講在 Shadow DOM 上使用 CSS 樣式實在是個有趣的大話題,事實上,這個話題太大以致於我準備將其拆成兩篇博文來闡述。html5

若是你想直奔主題,我列出個人這一篇博文 Shadow DOM CSS 小抄以供參考。web

今天咱們將學習在影子邊界(shadow boundary)上使用 CSS 樣式,以及如何給影子宿主(shadow hosts)添加樣式。瀏覽器

在今天開始以前,我想要感謝 Eric Bidelman 的這篇介紹 Shadow DOM 樣式添加的宏文(能夠戳中文譯版)。本文的大部分都是我對他這篇博文內容的實踐。若是有機會的話你必定要去讀一下HTLM5 Rocks 關於 Web Components 的所有文章app

技術支持

我建議你使用 Chrome v33+ 來實驗本文的例子,由於 33+ 的 Chrome 對我所描述的這些新特性都有瀏覽器的原生支持。dom

樣式封裝

機智的讀者可能會注意到在文章開始的簡介處我使用了一個新的術語——影子邊界。影子邊界表示分離常規 DOM (與影子 DOM 相對立的「光明」 DOM)與 shadow DOM 的壁障。影子邊界的主要好處就是防止主 DOM 中的樣式泄露到 shadow DOM 中。這就意味着即便你在主文檔中有一個針對所有 <h3> 標籤的樣式選擇器,這個樣式也不會不經你的容許便影響到 shadow DOM 的元素。ide

你說栗子呢?諾,來個栗子:組件化

example

<body>  
  <style>
    button {
      font-size: 18px;
      font-family: '華文行楷';
    }
  </style>
  <button>我是一個普通的按鈕</button>
  <div></div>

  <script>
    var host = document.querySelector('div');
    var root = host.createShadowRoot();
    
    root.innerHTML = '<style>button { font-size: 24px; color: blue; } </style>' +
                     '<button>我是一個影子按鈕</button>'
  </script>
</body>

shadow dom style

咱們整了兩個按鈕,一個是普通的 DOM,另外一個是 shadow DOM。注意頁面頂部的 <style> 標籤指示全部的 button 都要用行楷以及 18px 的字號。學習

<style>
  button {
    font-size: 18px;
    font-family: '華文行楷';
  }
</style>

因爲影子邊界的存在,第二個按鈕忽略掉這個樣式標籤並使用本身的樣式。因爲咱們沒有重寫 font-family 屬性,因此它使用了瀏覽器默認的字體來實現。

要記住影子邊界也保護主文檔不受 shadow DOM 樣式的侵襲。你可能注意到影子按鈕有一個藍色的color 屬性,可是原文檔中的按鈕仍是保持了它默認的顯示樣式。

這種做用域化(scoping)的特性實在是很是的「額妹子嚶」。咱們折騰了這麼些年樣式表,它的選擇範圍彷佛愈來愈大。你愈來愈難以向一個項目中添加新的樣式,由於你擔憂不當心把頁面中的那一塊搞崩掉。Shadow DOM 提供給咱們的樣式邊界意味着咱們終於能夠開始用一種更加局部的、特定組件化的方式來考慮和編寫咱們的 CSS。

宿主樣式(:host)

我常常把影子宿主想象成一棟建築物的外表。這棟建築物的內部有組件的所有運行方式,外面有一個好的門面。 許多狀況下你可能會想給這門面調整一下樣式,這就輪到 :host 選擇器出場了。

<body>  
  <style>
    .widget {
      text-align: center;
    }
  </style>

  <div class="widget">
    <p>Hello World!</p>
  </div>

  <script>
    var host = document.querySelector('.widget');
    var root = host.createShadowRoot();
    
    root.innerHTML = '<style>' +
                     ':host {' +
                     '  border: 2px dashed red;' +
                     '  text-align: left;' +
                     '  font-size: 28px;' +
                     '} ' +
                     '</style>' +
                     '<content></content>';
  </script>
</body>

enter image description here

儘管給咱們的組件添加一個紅色邊框看似沒啥,但這裏面但是發生了不少有趣的事情。首先,應用於:host 的樣式是繼承自 shadow DOM 裏的元素的。因此咱們的 <p> 標籤裏的字體大小有 28px。

同時注意到,頁面上的樣式能夠設置 :host 中的 text-align 的文本對齊方式爲居中。:host 的選擇器的優先級被設定爲低於頁面選擇器的優先級,因此若是有須要的話它能夠輕鬆的被頁面重寫樣式。在這個例子裏頁面樣式 .widget 擊敗了影子樣式 :host

宿主樣式中的類型選擇器

因爲 :host 是僞類選擇器(Pseudo Selector),咱們能夠將其應用於多個標籤上來改變咱們組件的外觀。咱們舉一個其餘的栗子來證實這一點。

<body>
  <p>個人段落</p>
  <div>個人 Div</div>
  <button>個人按鈕</button>

  <!-- Our template -->
  <template class="shadow-template">
    <style>
    :host(p) {
      color: blue;
    }

    :host(div) {
      color: green;
    }

    :host(button) {
      color: red;
    }

    :host(*) {
      font-size: 24px;
    }
    </style>
    <content select=""></content>
  </template>

  <script>
    // 給每個元素建立一個影子根
    var root1 = document.querySelector('p').createShadowRoot();
    var root2 = document.querySelector('div').createShadowRoot();
    var root3 = document.querySelector('button').createShadowRoot();

    // 對於每個影子根使用同一個模板
    var template = document.querySelector('.shadow-template');

    // 把每個模板嵌入影子根中,注意一下不一樣的 :host 樣式對顯示效果的影響
    root1.appendChild(document.importNode(template.content, true));
    root2.appendChild(document.importNode(template.content, true));
    root3.appendChild(document.importNode(template.content, true));
  </script>
</body>

enter image description here

因爲模板標籤在使用上要更簡單一些,所以我在這個例子中改用模板標籤來操做 Shadow DOM。

在上面的例子中能夠看到,咱們能夠利用 :host 選擇器來改變咱們組件的某一個特定標籤樣式。咱們還能夠根據類名、ID、屬性等等來進行匹配選擇——任何有效的 CSS 選擇器均可以正常工做。

比方說,若是你想寫一個自適應的組件,你能夠在 :host 中寫各類諸如 .widget-fixed 、.widget-flex.widget-fluid 的樣式,或者在表單元素的 :host 中寫 .valid 和 .error 樣式。

經過使用 * 選擇器,咱們能夠創造應用於所有 :host 元素的默認的樣式,正如在這個例子中咱們設置全部組件的 font-size 爲 24px。經過這一方式你能夠構建組件的基本外觀,而後在經過不一樣方式的選擇器給你的組件增光添彩。

那咱們怎麼基於宿主元素的父元素對它構建不一樣的主題呢?嚯嚯,咱們有一個專門實現主題化的選擇器!

主題化

<body>
  <div class="serious">
    <p class="serious-widget">
      你們好我十分的嚴肅    </p>
  </div>

  <div class="playful">
    <p class="playful-widget">
      漂亮的小云彩效果……    </p>
  </div>

  <template class="widget-template">
    <style>
    :host-context(.serious) {
      width: 250px;
      height: 50px;
      padding: 50px;
      font-family: '微軟雅黑';
      font-weight: bold;
      font-size: 24px;
      color: black;
      background: tomato;
    }

    :host-context(.playful) {
      width: 250px;
      height: 50px;
      padding: 50px;
      font-family: '華文行楷';
      font-size: 24px;
      color: white;
      background: deepskyblue;
    }
    </style>
    <content></content>
  </template>
  <script>
    var root1 = document.querySelector('.serious-widget').createShadowRoot();
    var root2 = document.querySelector('.playful-widget').createShadowRoot();
    var template = document.querySelector('.widget-template');

        root1.appendChild(document.importNode(template.content, true));
    root2.appendChild(document.importNode(template.content, true));
  </script>
</body>

enter image description here

使用 :host-context() 的語法咱們能夠基於內容元素修改咱們組件的外觀。這實在太簡潔啦!我確信你曾用過子類選擇器,例如 .parent > .child 這樣的,但你是否曾夢想有例如 .parent < .child 的這樣一個父類選擇器?如今你的夢想成真啦,固然這僅限於使用 shadow DOM。我在想有一天咱們是否是能看到這個語法也能在 CSS 中出現呢?

宿主樣式狀態

:host 標籤最好用的地方之一就是設置狀態的樣式,例如 :hover 和 :active。例如,咱們想在按鈕被用戶鼠標覆蓋的時候上加一個綠色的邊框,好辦!

<body>  
  <button>個人按鈕</button>

  <template class="button-template">
    <style>
      :host {
        font-size: 18px;
        cursor: pointer;
      }
      :host(:hover) {
        border: 2px solid green;
      }
    </style>
    <content></content>
  </template>
  
  <script>
    var host = document.querySelector('button');
    var root = host.createShadowRoot();
    var template = document.querySelector('.button-template');

        root.appendChild(template.content.cloneNode(true));
  </script>
</body>

enter image description here

這沒啥特別的,就是但願你多想一些場景。你還想到了其餘你可使用的場景麼?

結論

關於 Shadow DOM 的樣式我還想多說點,可是今天就到這裏吧,咱們明天繼續。和往常同樣歡迎來個人 twitter 艾特我或者給我留言,感謝閱讀!


譯者注:

因爲新概念有點多,列出一些參考博文和本身的看法,以饗讀者。

  • 模板標籤,HTML5Rocks的文章中譯本,結合這篇以及文中給出的做者本身博文的連接就能大概瞭解模板標籤的特性。這個標籤目前在非 IE 的瀏覽器下都獲得支持,主要有四個特性:

    本文中利用模板爲 shadow DOM 填充內容,省去了經過 JS 加載的麻煩。注意文中使用.content 屬性來導出模板的內容,並經過 importNode 方式對模板進行了深拷貝。

    1. 惰性:在使用前不會被渲染;

    2. 無反作用:在使用前,模板內部的各類腳本不會運行、圖像不會加載等;

    3. 內容不可見:模板的內容不存在於文檔中,使用選擇器沒法獲取;

    4. 可被放置於任意位置:即便是 HTML 解析器不容許出現的位置,例如做爲 <select> 的子元素。

  • :host-context 的做用,這個選擇器的實際用法是用來選擇影子宿主的祖先元素的,最大的用處大概就是主題的設置。比方個人組件有各類狀態,可是不少狀況用戶實際操做的多是組件外部的父元素,這個選擇器的目的就是經過更改父元素的樣式狀態就能完成組件狀態的變化。

相關文章
相關標籤/搜索