最近一個內部系統(tǒng)可視化設計器的布局在用戶升級 chrome 到 53 版本之后,里面的坑位增加按鈕消失了畔乙,經過排查發(fā)現(xiàn)君仆,坑位的標簽使用了slot
,而坑位中的按鈕是通過 css 的 empty 偽類實現(xiàn)的牲距,即坑位為空時返咱,出現(xiàn)增加按鈕,但現(xiàn)在為什么突然無效了呢牍鞠?
把slot
的名字換成其他標簽名咖摹,發(fā)現(xiàn)樣式頓時生效,于是猜測是slot
作為特殊標簽被 chrome 設置了特定的行為难述,經過查閱資料發(fā)現(xiàn)slot
是 shadow dom 中的內置標簽萤晴。
現(xiàn)在我們來了解下 shadow dom 以及與其相關的兩個特殊標簽 template 和 slot 。
何為shadow dom
shadow dom胁后,顧名思義店读,影子節(jié)點,它是 web components 規(guī)范的一個子集攀芯,主要為了解決dom對象的封裝屯断,通常的dom,其js行為和css樣式極易受到別的代碼的干擾侣诺,但shadow dom規(guī)定了只有其宿主才可定義其表現(xiàn)殖演,外部的api是無法獲取到shadow dom中的東西。
由于shadow dom是影子元素紧武,因此其必須捆綁一個宿主元素剃氧,宿主元素事實上成為了“傀儡”,宿主元素的內容將被隱藏阻星,而shadow dom的內容將展示出來朋鞍,以下是一個例子:
html:
<div id="con">
沒什么卵用的文字
</div>
js:
var host = document.querySelector('#con');
var root = host.attachShadow({mode:'open'});//為宿主附加一個影子元素
root.innerHTML = "我來自shadow dom";//為影響元素附上內容,shadow dom的api和普通dom的大致相同
最終效果:
我來自shadow dom
可以看到妥箕,宿主的內容確實被掩蓋了滥酥,然而通過chrome的devtools,可以看到宿主的原內容以及背后的shadow dom:

shadow dom中的template
前面說了畦幢,shadow dom可以實現(xiàn)dom的隔離坎吻,比如樣式的封裝,那么如何實現(xiàn)呢宇葱?shadow規(guī)定了一種名為template
的標簽瘦真,這種標簽類似我們經常用的<script type='tpl'>
刊头,它不會被解析為dom樹的一部分,template的內容可以被塞入到shadow dom中并且反復利用诸尽,在template中可以設置style原杂,但只對這個template中的元素有效,看下示例:
html:
<style>
span {
background-color:blue;/*設置頁面所有span背景為藍色您机,然而對shadow dom沒什么卵用*/
}
</style>
<div id="con">
沒什么卵用的文字
</div>
<template id="tpl">
<style>
span {
color:red;
}
</style>
<span>hello world</span>
</template>
js:
var host = document.querySelector('#con');
var root = host.attachShadow({mode:'open'});
var con = document.getElementById("tpl").content.cloneNode(true);
root.appendChild(con);
效果截圖:
<span style='color:red'>hello world</span>
可以看到穿肄,template
的內容被塞入到宿主,并且其文案被設置為紅色际看,而body 中對 span 設置為藍色背景卻沒有生效咸产;另外這里要注意document.getElementById("tpl").content
中的content
屬性,它是template標簽的特有屬性仲闽,你可以通過嗅探該屬性來判斷瀏覽器是否支持shadow dom和template
標簽脑溢。
shadow dom的slot標簽
由于shadow dom的內容會掩蓋宿主的內容,那么現(xiàn)在問題來了蔼囊,我就是想把宿主的內容顯示出來怎么辦焚志?
最新的shadow dom草案支持了一個叫slot標簽的東西,slot是一個插槽畏鼓,一個坑位酱酬,可以在template中定義坑位,然后宿主中的內容可以標記屬于哪一個坑位云矫,這樣一個蘿卜一個坑膳沽,宿主的內容就會被正確地插入到template所標記的位置去,還是來看一個例子:
html:
<div id="con">
沒什么卵用的文字
<span slot="main1">
坑位1
</span>
<span slot="main2">
坑位2
</span>
沒什么卵用的文字 </div>
<template id="tpl">tpl begin
<slot name="main1">
</slot>
<slot name="main2">
</slot>
tpl end
</template>
js:
var host = document.querySelector('#con');
var root = host.attachShadow({mode:'open'});
var con = document.getElementById("tpl").content.cloneNode(true);
root.appendChild(con);
最終的效果是:
tpl begin 坑位1 坑位2 tpl end
可以看到让禀,宿主中的兩個span分別插入到了其標記的slot坑位中挑社。在slot出現(xiàn)之前,仍然可以實現(xiàn)類似的功能巡揍,只不過標簽名叫content痛阻。
結語
現(xiàn)在我們來解釋一下文章開頭出現(xiàn)的問題的原因,由于 slot標簽僅僅是一個占位符而已腮敌,其最終會被宿主標記了該位置的內容替換(注意是替換阱当,而不是插入),因此沒必要對slot標簽設置樣式糜工,這就是為啥chrome 53忽略其樣式的原因弊添,無獨有偶,最新版的ios 10默認瀏覽器也會隱藏slot捌木,因為slot中并不需要顯示任何東西油坝。
借鑒vue的思想,我們在編譯階段把slot編譯成div,從而解決了這個問題澈圈,順便提醒一下彬檀,shadow dom是非常新的技術,目前還處在試驗階段瞬女,而且標準還不確定凤覆,請不要在生產環(huán)境使用。