理解Shadow DOM

1. 什么是Shadow DOM?

Shadow DOM 如果按照英文翻譯的話可以理解為 影子DOM, 何為影子DOM呢?可以理解為一般情況下使用肉眼看不到的DOM結構,那如果一般情況下看不到的話,那也就是說我們無法直接控制操縱的DOM結構。
Shadow DOM 它是HTML的一個規范,它允許在文檔(document)渲染時插入一顆DOM元素子樹,但是這個子樹不在主DOM樹中。
它允許瀏覽器開發者封裝自己的HTML標簽、css樣式和特定的javascript代碼、同時開發人員也可以創建類似 <input>、<video>、<audio>等、這樣的自定義的一級標簽。創建這些標簽內容相關的API,可以被叫做 Web Component。

如上基本解釋,讓我們很難理解,因此我們可以先看下如下一個 input標簽的demo吧。

html代碼如下:

復制代碼

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}input::-webkit-input-placeholder {color: red;}</style></head><body><div id="app"><input type="number" placeholder="請輸入內容" /></div></body>
</html>

復制代碼

然后我們在瀏覽器查看如下所示:

如上代碼可以看到,我們使用 input::-webkit-input-placeholder{color: red;} 偽類后,input中的placeholder字體顏色發生改變了,但是我們的input元素的結構并沒有看到偽類相關的html結構。

為了能看到基本結構,我們只需要在chrome瀏覽器中,打開開發者工具,點擊右上角的 "Settings"按鈕,勾選 "Show use agent shadow DOM". 后 如下圖所示:

然后我們再來看下input元素的基本代碼結構如下看到:

如上截圖所示,我們可以看到 "請輸入內容" 中的div元素上有一個屬性為 pseudo="-webkit-input-placeholder", 因此我們使用 input元素的偽類 input::-webkit-input-placeholder{} 這樣就可以控制元素的樣式了。

2. ShadowDOM 存在的意義?

首先我們來看下Shadow-dom 基本的結構如下:

Document:?是document文檔對象。
shadow-host:?Shadow DOM的容器元素,即:它是Shadow DOM的一個宿主元素。比如:<input />、<audio>、<video> 標簽;就是shadow-dom的宿主元素。
shadow-root:?Shadow DOM的根節點。通過createShadowRoot返回的文檔片段被稱為 shadow-root, 它和它的后代元素,都會對用戶隱藏。但是它會在瀏覽器中被渲染的,也就是說它是存在的,但是一般情況下在瀏覽器中是不顯示出來的。在chrome瀏覽器中,如上我們可以看到的。
contents:?Shadow DOM包含的子節點樹結構。它包含 <input />、<audio>、<video>等標簽中各子組件的DOM的具體實現。

那么ShadowDOM存在的意義是?

我們都知道像React或Vue這樣的都有組件的概念,比如element-ui等這樣的vue組件。但是我們常用的input、audio、video、等這些元素,其實它也是以組件的形式存在的,即:HTML Web Component. 即這些都有 Shadow DOM。

因此我們可以認為像 input, audio, video等這些元素也是以組件的形式存在的。那么這些組件內部是由一些HTML標簽組成的。
這些元素組成了DOM樹的子樹。但是當我們使用input,audio,或video等這些元素組件的時候,都會知道該子樹的結構,當我們訪問網頁DOM結構的時候,這些子樹都會暴露出來,當我們使用css樣式去改變DOM的樣式的時候,如果DOM的類名和該子樹的類名相同的話,會和子樹的樣式產生沖突,并且我們使用控件的時候,我們并不關心控件的內部結構,只關心控件本身,因此我們需要將控件的內部信息封裝起來。因此 W3C提出了 ShadowDOM的概念,ShadowDOM可以使一些DOM節點在特定范圍內可見,在網頁中是不可見的。但是在頁面渲染的時候也會渲染該ShadowDOM。
也可以看這篇文章介紹

ShadowDOM在各個瀏覽器的支持程度呢?

查看Can I Use(https://caniuse.com/#search=shadowDOM) 可以看到它在瀏覽器下的支持程度,如下所示:

3. 如何控制 shadow-dom?

既然W3C提出了ShadowDOM的概念,并且在網頁中一般是不可見的,那么我們是否可以控制該 shadow-dom呢?
下面我們以 <video>標簽來講解吧,比如如下HTML代碼:

復制代碼

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}</style></head><body><div id="app"><video src="http://www.w3school.com.cn/i/movie.ogg" controls="controls">your browser does not support the video tag</video></div></body>
</html>

復制代碼

3.1 使用偽類來控制 shadow-dom的樣式。

首先看如上代碼顯示的效果圖如下:

在chrome瀏覽器下,我們查看 shadow-dom 結構,可以看到每個元素都加上了一個pesudo 這樣的屬性。我們可以通過這些屬性使用偽類來控制他們的樣式。如下圖所示:

基本樣式如下:

video::-webkit-media-controls-play-button {background-color: red;
}

我們給播放按鈕添加了一個背景顏色為紅色。

注意:很遺憾的是,只有chrome瀏覽器下支持,其他瀏覽器下并不支持,雖然大部分瀏覽器下支持shadow-dom。

4. 使用javascript如何來創建Shadow DOM

4.1 使用 createShadowRoot()來創建Shadow DOM。

如下基本代碼:

復制代碼

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}.shadow-child {color: red;}</style></head><body><div id="app"><div class="shadow-cls">hello, kongzhi</div></div><script type="text/javascript">// 1. 獲取影子宿主 shadow hostvar shadowHost = document.querySelector('.shadow-cls');// 2. 創建影子 shadow rootvar shadowRoot = shadowHost.createShadowRoot();// 3. shadow root 作為影子樹的第一個節點,其他的節點,比如如下的p節點都是它的子節點。shadowRoot.innerHTML = '<p class="shadow-child">我是子節點</p>';</script></body>
</html>

復制代碼

然后我們查看效果如下:

如上我們可以看到,給影子樹子節點設置css樣式,但是并沒有生效,那是因為影子宿主(shadow host)和影子根(shadow root)之間存在影子邊界。影子邊界保證DOM編寫的css和javascript代碼都不會影響到ShadowDOM.當然反之也是一樣,互不影響。

4.2 理解<content> 和 <template> 的用法。

<content>標簽可以把來自DOM文檔的內容添加到shadow DOM的內容被叫做分布節點。

<content>有一個select屬性來告訴<content>標簽要插入的內容,select屬性值是一個使用CSS選擇器來獲取想要的內容。
選擇器包括類選擇器、元素選擇器等。

比如如下代碼:

復制代碼

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}</style></head><body><div id="app"><div class="shadow-cls"><em class="shadowhost-content1">我是空智</em><em class="shadowhost-content2">我是龍恩</em></div><!-- 下面是一個模板 template --><template class="template"><div><h1>哈哈,</h1><content select=".shadowhost-content1"></content>我來了<content select=".shadowhost-content2"></content>!</div></template></div><script type="text/javascript">// 1. 獲取影子宿主 shadow hostvar shadowHost = document.querySelector('.shadow-cls');// 2. 創建影子 shadow rootvar shadowRoot = shadowHost.createShadowRoot();// 3. 獲取模板元素var template = document.querySelector('.template');/*4. template.content 會返回一個文檔片段。5. 使用 document.importNode獲取節點,true參數表示深度克隆*/shadowRoot.appendChild(document.importNode(template.content, true));</script></body>
</html>

復制代碼

執行結果如下:

我們也可以通過如下打印:

console.log(template.innerHTML);   // 獲取完整的HTML片段
console.log(template.content);  // 返回一個文檔片段#document-fragment
console.log(template.childNodes);  // 返回[],說明childNodes無效

這些的信息的區別,如下所示:

4.3 shadowDOM樣式

1. 宿主樣式:
在shadow DOM中利用 :host定義宿主的樣式。:host 是偽類選擇器,給所有的宿主添加樣式可以如下代碼::host 或 :host(*); 如果想給單獨的宿主添加樣式可以 :host(xx); 其中xx是宿主的標簽或類選擇器等。并且:host還可以配合 :hover、:active等狀態來設置樣式,如下代碼:

復制代碼

/* 定義宿主樣式 :host */
:host {color: red;
}
/* 定義宿主hover狀態下的樣式 */
:host(:hover) {color: black;
}

復制代碼

2. ::shadow

影子邊界為了保證DOM編寫的css或javascript不影響到shadowDOM。如果我們想讓 shadowDOM添加一些樣式,可以使用 ::shadow這樣的。

3. /deep/

::shadow選擇器有一個缺陷是它只能穿透一層影子邊界,如果我們在一個影子樹中嵌套了多個影子樹的話,那么我們需要使用/deep/ 這樣的來編寫css樣式。

4. ::content

我們通過 <content> 標簽把主文檔中的元素添加到shadowDOM的內容被叫做分布節點。分布節點的樣式渲染需要用到::content。比如我們想給節點為em標簽的話,我們直接寫 em {} 這樣的是不會生效的,我們要寫成?::content > em {}. 比如如下代碼:

/* 分布節點的樣式渲染需要用到 ::content */
::content > em {color: blue;background: red;
}

下面我們再來看一下如下demo。

復制代碼

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}</style></head><body><div id="app"><div class="shadow-cls"><em class="shadowhost-content1">我是空智</em><em class="shadowhost-content2">我是龍恩</em></div><!-- 下面是一個模板 template --><template class="template"><style>/* 定義宿主樣式 :host */:host {color: red;}/* 定義宿主hover狀態下的樣式 */:host(:hover) {color: black;}/* 分布節點的樣式渲染需要用到 ::content */::content > em {color: blue;background: red;}</style><div><h1>哈哈,</h1><content select=".shadowhost-content1"></content>我來了<content select=".shadowhost-content2"></content>!</div></template></div><script type="text/javascript">// 1. 獲取影子宿主 shadow hostvar shadowHost = document.querySelector('.shadow-cls');// 2. 創建影子 shadow rootvar shadowRoot = shadowHost.createShadowRoot();// var shadowRoot = shadowHost.attachShadow({mode: 'open'});// 3. 獲取模板元素var template = document.querySelector('.template');/*4. template.content 會返回一個文檔片段。5. 使用 document.importNode獲取節點,true參數表示深度克隆*/shadowRoot.appendChild(document.importNode(template.content, true));</script></body>
</html>

復制代碼

然后我們再在chrome瀏覽器下看下結果如下所示:

注意:上面的demo在chrome瀏覽器下會生效,在firefox或safari是不支持的。因此如果在平時的開發中我們需要引入對應的庫進來才會支持。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247443.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247443.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247443.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

046 實例11-自動軌跡繪制

目錄 一、"自動軌跡繪制"問題分析1.1 問題分析1.2 自動軌跡繪制二、"自動軌跡繪制"實例講解2.1 自動軌跡繪制2.2 數據接口定義2.3 數據文件三、"自動軌跡繪制"舉一反三3.1 理解方法思維3.2 應用問題的擴展一、"自動軌跡繪制"問題分析 …

bootstrap-select采坑

bootstrap-select采坑 1.class"selectpicker" 普通的下拉框功能 2.title"請選擇城市名稱" title的作用與palcehoder一樣。 3.select class"selectpicker" multiple selectpicker和multiple屬性的搭配使用可實現多選 4.data-live-search"tru…

對vue虛擬dom的研究

Vue.js通過編譯將template 模板轉換成渲染函數(render ) &#xff0c;執行渲染函數就可以得到一個虛擬節點樹在對 Model 進行操作的時候&#xff0c;會觸發對應 Dep 中的 Watcher 對象。Watcher 對象會調用對應的 update 來修改視圖。這個過程主要是將新舊虛擬節點進行差異對比…

element-ui之dialog組件title插槽的使用

dialog對話框組件title屬性的slot使用方法 使用背景 需要單獨控制title中某個數據顯示及樣式&#xff0c;footer也一樣 <el-dialog// 也可以這樣寫,但是沒有辦法單獨控制name age的顯示// title"name age"title"提示":visible.sync"dialogVisi…

css3自適應布局單位vw,vh

視口單位(Viewport units) 什么是視口&#xff1f; 在桌面端&#xff0c;視口指的是在桌面端&#xff0c;指的是瀏覽器的可視區域&#xff1b;而在移動端&#xff0c;它涉及3個視口&#xff1a;Layout Viewport&#xff08;布局視口&#xff09;&#xff0c;Visual Viewport…

python 操作 elasticsearch-7.0.2 遇到的問題

錯誤一&#xff1a;TypeError: search() got an unexpected keyword argument doc_type&#xff0c;得到不預期外的參數 解決方法&#xff1a;elasticsearch7里不用文檔類型&#xff0c;所以去掉 doc_typecredit_data 錯誤二&#xff1a;RequestError(400, illegal_argument_ex…

用到的Shell

sed 1i 添加的內容 file #這是在第一行前添加字符串 sed $i 添加的內容 file #這是在最后一行行前添加字符串 sed $a添加的內容 file #這是在最后一行行后添加字符串 sed -i s/.*/行首添加內容&行尾添加內容/ 文件名 //每一行 sed -i $a新增的一行 tars_build_tar.sh a…

如何解決瀏覽器縮小出現橫向滾動條時網頁背景圖出現空白的問題

原因&#xff1a; 當窗口縮小時&#xff0c;瀏覽器默認100%寬度為瀏覽器窗口的寬度。而忽略了下部內容層固定寬度(1024px)。從而出現了固定寬度大于100%寬度的現象。瀏覽以此理解來解析頁面&#xff0c;就出現了容器寬度理解上的差異&#xff0c;出現了一個非常奇特的BUG。 解…

前端設計模式

1. 單例模式 2.裝飾器模式 轉載于:https://www.cnblogs.com/lyraLee/p/11210985.html

區別 (function($){...})(jQuery)、$(function(){ })和$.fn

一、(function($){…})(jQuery) 首先function(arg){...}定義了一個匿名函數&#xff0c;參數為arg,而調用時需要在函數后面寫上括號和實參&#xff0c;由于操作符的優先級&#xff0c;函數本身也需要括號&#xff0c;也就成了&#xff1a; &#xff08;function(arg){...}&…

git 清除緩存

清除git緩存 git config --local --unset credential.helpergit config --global --unset credential.helpergit config --system --unset credential.helper保存git緩存 git config --global credential.helper store轉載于:https://www.cnblogs.com/zhouyideboke/p/11211650.…

網頁里如何使用js禁用控制臺

網頁里如何禁用右擊事件&#xff1f;使用jQuery&#xff0c;幾句代碼就可以搞定了 document.oncontextmenu function(){return false;} 簡單示例&#xff1a; js實現&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><…

Java生鮮電商平臺-訂單中心服務架構與異常訂單邏輯

Java生鮮電商平臺-訂單中心服務架構與異常訂單邏輯 訂單架構實戰中闡述了訂單系統的重要性&#xff0c;并從訂單系統的信息架構和流程上對訂單系統有了總體認知&#xff0c;同時還穿插著一些常見的訂單業務規則和邏輯。上文寫到訂單的拆單部分時擱置了&#xff0c;現在接上文繼…

Vuex的全面用法總結

1. vuex簡介 vuex是專門用來管理vue.js應用程序中狀態的一個插件。他的作用是將應用中的所有狀態都放在一起&#xff0c;集中式來管理。需要聲明的是&#xff0c;這里所說的狀態指的是vue組件中data里面的屬性。了解vue的同學應該是明白data是怎么回事的吧&#xff0c;如果不懂…

vue中通過第三方代理解決跨域問題

最近在學node&#xff0c;遇到了跨域的問題&#xff0c;來記錄下方法 首頁服務端的框架是通過express-generator 搭建起來的 npm install -g express-generator 具體接下來的細節不多說&#xff0c;今天主要說跨域 的問題 左側為服務端項目結構&#xff0c;www為可執行文件&am…

使用V-chart時配置踩過的一些坑

如何配置圖表信息 echart的配置項可謂是相當的海量&#xff0c;能不看就不看。而v-chart對其進行了不少的簡化&#xff0c;所以我們想要自定義一個圖表時&#xff0c;最好按照以下步驟來檢查&#xff1a; 圖表私有屬性 v-chart每一個圖表都有自己獨有的設置項&#xff0c;想…

ZDOzMRVAOq

11 轉載于:https://www.cnblogs.com/wc529065/p/11212226.html

vue 出現Elements in iteration expect to have 'v-bind:key' directives

是由于eslint檢測出現bug 解決方法有兩種 v-for 后添加 :keyitem <li v-for"item in list" :key"item"> 在build處關閉eslint檢測 ...(config.dev.useEslint ? [createLintingRule()] : []),

requestAnimationFram

window.requestAnimationFrame() 告訴瀏覽器——你希望執行一個動畫&#xff0c;并且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。該方法需要傳入一個回調函數作為參數&#xff0c;該回調函數會在瀏覽器下一次重繪之前執行 注意&#xff1a;若你想在瀏覽器下次重繪之…

vue/return-in-computed-property Enforce that a return statement is present in computed property

此規則強制return語句在computed屬性中得完整存在。 <script> export default {computed: {/* ? GOOD */foo () {if (this.bar) {return this.baz} else {return this.baf}},bar: function () {return false},/* ? BAD */baz () {if (this.baf) {return this.baf}},ba…