<video controls autoplay name="media">
<source id="mp4" src="trailer.mp4" type="video/mp4">
</video>複製代碼
上面這是最簡單的視屏標籤,裏面有默認的音量等按鍵。在源代碼中根本沒有一點痕跡。那這些節點是從哪裏來的?
這就是shadow DOM,視屏的控件在瀏覽器中真實面目以下:
javascript
發現#shadow-root
是灰色的,這是瀏覽器爲了代表在shadow DOM中,表明頁面其餘的部分的內容不會對內部產生影響(可用特定方式穿透,後文會說到)css
內容具體指css選擇器和javascript代碼html
簡而言之,Shadow DOM 是一個 HTML 的新規範,其容許開發者封裝HTML組件(相似vue組件,將html,css,js獨立部分提取)。vue
Bootstrap的名字你必定不陌生,代碼通常以下:java
<ul class="media-list">
<li class="media">
<div class="media-left">
<a href="#">
![](...)
</a>
</div>
<div class="media-body">
<h4 class="media-heading">Media heading</h4>
</div>
</li>
</ul>複製代碼
很是的簡單好用,可是這些東西你並無深刻了解,每每結構變複雜以後,都是一堆模板,修改是一個很難的問題,牽一髮而動全身。
這種狀況下shadow DOM的優點十分巨大,你能夠這麼寫模板bootstrap
// 我是一個簡潔的模板
<bootstrap-media-list>
<a href="#">
![](...)
</a>
<h4 class="media-heading">Media heading</h4>
</bootstrap-media-list>複製代碼
固然想實現這麼寫還須要一些js,css配合才行瀏覽器
先運行一個例子bash
<div class="widget">Hello, world!</div>
<script>
var host = document.querySelector('.widget');
var root = host.createShadowRoot();
root.textContent = '我在你的 div 裏!';
</script>複製代碼
只渲染影子根中的內容基本沒有實用的地方,但能自由的渲染宿主節點中的內容的話就可讓頁面展示更靈活。咱們須要content
標籤app
<div class="pokemon">胖丁</div>
<template class="pokemon-template">
<h1>一隻野生的<content></content>出現了!</h1>
</template>
<script>
var host = document.querySelector('.pokemon');
var root = host.createShadowRoot();
var template = document.querySelector('.pokemon-template');
root.appendChild(document.importNode(template.content, true));
</script>複製代碼
<content>
標籤建立了一個
插入點 將
.pokemon
裏面的文本投影出來,多個內容匹配時能夠實用
select
屬性指定
<div class="host">
<p>大慈大悲,由諸葛亮進化而來。</p>
<span class="name">觀音姐姐獸</span>
</div>
<template class="root-template">
<dl>
<dt>名字</dt>
<dd><content select=".name"></content></dd>
</dl>
<p><content select=""></content></p>
</template>
<script>
var host = document.querySelector('.host');
var root = host.createShadowRoot();
var template = document.querySelector('.root-template');
root.appendChild(template.content);
</script>複製代碼
select
屬性相似選擇器的形式渲染宿主節點中匹配元素的投影。這種形式不但能夠改變DOM流的順序也可讓佈局變得靈活。
<content select=""></content>
是一種貪心匹配,把宿主節點中全部未被匹配的內容所有投影。須要注意的是把貪心匹配放在最前面會把全部的節點投影而且以後的select不會再獲取到被其投影的內容。
<content></content>
<conent select=""></conent>
<content select="*"></content>
先看一個簡單的例子dom
<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>複製代碼
shadow DOM
樣式和正常DOM流中的樣式不相互干擾。這是一種
做用域化的體現
,不用再擔憂樣式的相互衝突。
:host
是僞類選擇器選擇宿主節點,咱們能夠擴展一下上面的例子
<style>
p {
font-size: 12px;
}
</style>
<p>個人文本</p>
<button>個人按鈕</button>
<template class="shadow-template">
<style>
:host(p) {
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('button').createShadowRoot();
var template = document.querySelector('.shadow-template');
root1.appendChild(document.importNode(template.content, true));
root2.appendChild(document.importNode(template.content, true));
</script>複製代碼
:host
選擇器中可使用任意合法選擇器,*
應用於全部.parent > .child
,可是咱們還能經過:host-context
實現.parent < .child
以下<div class="serious">
<p class="serious-widget">
serious-widget
</p>
</div>
<div class="playful">
<p class="playful-widget">
playful-widget
</p>
</div>
<template class="widget-template">
<style>
:host-context(.serious) {
width: 250px;
height: 50px;
background: tomato;
}
:host-context(.playful) {
width: 250px;
height: 50px;
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>複製代碼
上面的效果就很是不錯了,能夠進行動態組件構建
ps: 僞類,僞元素選擇器也能夠直接使用,效果和正常節點中一致
在使用 shadow DOM 的時候應該確保內容和表現的分離,也就是說文本應該來自頁面而不是埋在 shadow DOM 的模板裏。因此咱們須要在模板中對分佈式節點進行渲染。
<div class="widget">
<button>分佈節點碉堡啦!</button>
</div>
<template class="widget-template">
<style>
::content > button {
color: white;
background: tomato;
border-radius: 10px;
border: none;
padding: 10px;
}
</style>
<content select=""></content>
</template>
<script>
var root = document.querySelector('.widget').createShadowRoot();
var template = document.querySelector('.widget-template');
root.appendChild(document.importNode(template.content, true));
</script>複製代碼
咱們能夠在掛載節點中使用::shadow
,好比
<style>
.sign-up::shadow #username{
font-size: 20px;
border: 1px solid red;
}
</style>
<div class="sign-up"></div>
<template class="sign-up-template">
<style>
#username{
font-size: 12px;
}
</style>
<div>
<input type="text" id="username" placeholder="用戶名">
</div>
</template>
<script>
var root = document.querySelector('.sign-up').createShadowRoot();
var template = document.querySelector('.sign-up-template');
root.appendChild(document.importNode(template.content, true));
</script>複製代碼
不過缺點是隻能穿透一層,但咱們還有一個神器!
<style>
#foo /deep/ button {
color: red;
}
</style>
<div id="foo"></div>
<template>
<div id="bar"></div>
</template>
<script>
var root1 = document.querySelector('#foo').createShadowRoot();
var template = document.querySelector('template');
root1.appendChild(document.importNode(template.content, true));
var root2 = root1.querySelector('#bar').createShadowRoot();
root2.innerHTML = '<button>點我點我</button>';
</script>複製代碼
window
上<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 root = document.querySelector('#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>複製代碼
能夠看到在影子節點的事件被宿主節點代理。
在監聽如下事件時會被阻塞在影子節點的根:
aborterror
select
change
load
reset
reset
resize
scroll
selectstar
<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 root = document.querySelector('#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>複製代碼
事件影子節點的根上被阻止,沒法冒泡到ducoment
,因此沒法監聽。
分佈節點指以前經過<content>
標籤將宿主節點的內容投影,分佈節點不會發生上面的阻塞狀況,由於這個只是一個投影實際的內容仍是掛載在宿主節點上。