摘要:書(shū)接上文瀏覽器內(nèi)核之資源加載與網(wǎng)絡(luò)棧本文介紹的模型之后,深入的核心部分,剖析的解釋器是如何將從網(wǎng)絡(luò)或者本地文件獲取的字節(jié)流轉(zhuǎn)成內(nèi)部表示的結(jié)構(gòu)樹(shù)。事件處理最重要就是事件捕獲和事件冒泡這兩種機(jī)制。
微信公眾號(hào):愛(ài)寫(xiě)bugger的阿拉斯加前言
如有問(wèn)題或建議,請(qǐng)后臺(tái)留言,我會(huì)盡力解決你的問(wèn)題。
此文章是我最近在看的【W(wǎng)ebKit 技術(shù)內(nèi)幕】一書(shū)的一些理解和做的筆記。
而【W(wǎng)ebKit 技術(shù)內(nèi)幕】是基于 WebKit 的 Chromium 項(xiàng)目的講解。
書(shū)接上文 瀏覽器內(nèi)核之資源加載與網(wǎng)絡(luò)棧
本文介紹 W3C 的 DOM 模型之后,深入 WebKit 的核心部分,剖析 WebKit 的 HTML 解釋器是如何將從網(wǎng)絡(luò)或者本地文件獲取的字節(jié)流轉(zhuǎn)成內(nèi)部表示的結(jié)構(gòu) --- DOM 樹(shù)。
1. DOM 模型 1.1.1 DOM 標(biāo)準(zhǔn)DOM (Document Object Model)的全稱是文檔對(duì)象模型,它可以以一種獨(dú)立于平臺(tái)和語(yǔ)言的方式訪問(wèn)和修改一個(gè)文檔的內(nèi)容和結(jié)構(gòu)。這里的文檔可以是 HTML 文檔、XML 文檔或者 XHTML 文檔。DOM 以面向?qū)ο蟮姆绞絹?lái)描述文檔,在 HTML 文檔中,Web 開(kāi)發(fā)者可以使用 JavaScript 語(yǔ)言來(lái)訪問(wèn)、創(chuàng)建、刪除或者修改 DOM 結(jié)構(gòu),其主要目的是動(dòng)態(tài)改變 HTML 文檔的結(jié)構(gòu)。
使用 DOM 表示的文檔被描述成一個(gè)樹(shù)形結(jié)構(gòu),使用 DOM 的接口可以對(duì) DOM 樹(shù)結(jié)構(gòu)進(jìn)行操作。
每一級(jí)的版本都對(duì)以前的版本進(jìn)行了補(bǔ)充并伴隨新功能的加入,每個(gè)版本都對(duì) DOM 的不同部分進(jìn)行了定義。
1.1.2 DOM 樹(shù) 1.1.2.1 結(jié)構(gòu)模型DOM 結(jié)構(gòu)構(gòu)成的基本要素是 “節(jié)點(diǎn)” ,而文檔的 DOM 結(jié)構(gòu)就是由層次化的節(jié)點(diǎn)組成。在 DOM 模型中,節(jié)點(diǎn)的概念很寬泛,整個(gè)文檔(Document )就是一個(gè)節(jié)點(diǎn),稱為文檔節(jié)點(diǎn)。HTML 中的標(biāo)記(Tag)也是一種節(jié)點(diǎn),稱為元素(Element)節(jié)點(diǎn)。還有一些其他類(lèi)型的節(jié)點(diǎn),例如 屬性節(jié)點(diǎn)(標(biāo)記的屬性)、Entity 節(jié)點(diǎn)、ProcessingIntruction 節(jié)點(diǎn)、CDataSection 節(jié)點(diǎn)、注釋?zhuān)–omment)節(jié)點(diǎn)等。
1.1.2.2 DOM 樹(shù)眾多的節(jié)點(diǎn)按照層次組織構(gòu)成一個(gè) DOM 樹(shù)結(jié)構(gòu)。
如圖 5 - 4
DOM 樹(shù)的根就是 HTMLDocument , HTML 網(wǎng)頁(yè)中的標(biāo)簽則被轉(zhuǎn)換成一個(gè)個(gè)的元素節(jié)點(diǎn)。同數(shù)據(jù)結(jié)構(gòu)中的樹(shù)形結(jié)構(gòu)一樣,這些節(jié)點(diǎn)之間也存在父子或兄弟關(guān)系。
1.2 HTML 解釋器 1.2.1 解釋過(guò)程HTML 解釋器的工作就是將網(wǎng)絡(luò)或者本地磁盤(pán)獲取的 HTML 網(wǎng)頁(yè)和資源從字節(jié)流解釋成 DOM 樹(shù)結(jié)構(gòu)。這一過(guò)程大致可以理解成圖 5-5所述的步驟。
這過(guò)程中,WebKit 內(nèi)部對(duì)網(wǎng)頁(yè)內(nèi)容在各個(gè)階段的結(jié)構(gòu)表示。 WebKit 中這一過(guò)程如下:首先是字節(jié)流,經(jīng)過(guò)解碼之后是字符流,然后通過(guò)詞法分析器會(huì)被解釋成詞語(yǔ)(Tokens),之后經(jīng)過(guò)語(yǔ)法分析器構(gòu)建成節(jié)點(diǎn),最后這些節(jié)點(diǎn)被組建成一棵 DOM 樹(shù)。
1.2.2 詞法分析在進(jìn)行詞法分析之前,解釋器首先要做的事情就是檢查該網(wǎng)頁(yè)內(nèi)容使用的編碼格式,以便后面使用合適的解碼器。如果解釋器在 HTML 網(wǎng)頁(yè)中找到了設(shè)置的編碼格式, WebKit 會(huì)使用相應(yīng)的解碼器來(lái)將字節(jié)流轉(zhuǎn)換成特定格式的字符串。如果沒(méi)有特殊格式,詞法分析器 HTMLTokenizer 類(lèi)可以直接進(jìn)行詞法分析。
詞法分析的工作都是由 HTMLTokenizer 來(lái)完成 ,簡(jiǎn)單來(lái)說(shuō),它就是一個(gè)狀態(tài)機(jī)---輸入的是字符串,輸出的是一個(gè)個(gè)詞語(yǔ)。因?yàn)樽止?jié)流可能是分段的,所以輸入的字符串可能也是分段的,但是這對(duì)詞法分析器來(lái)說(shuō)沒(méi)有什么特別之處,它會(huì)自己維護(hù)內(nèi)部的狀態(tài)信息。
詞法分析器的主要接口是 “nextToken” 函數(shù),調(diào)用者只需要關(guān)鍵字符串傳入,然后就會(huì)得到一個(gè)詞語(yǔ),并對(duì)傳入的字符串設(shè)置相應(yīng)的信息,表示當(dāng)前處理完的位置,如此循環(huán),如果詞法分析器遇到錯(cuò)誤,則報(bào)告狀態(tài)錯(cuò)誤碼,主要邏輯在圖 5-8 中給予了描述。
對(duì)于 “nextToken” 函數(shù)的調(diào)用者而言,它首先設(shè)置輸入需要解釋的字符串,然后循環(huán)調(diào)用 NextToken 函數(shù),直到處理結(jié)束。 “nextToken” 方法每次輸出一個(gè)詞語(yǔ),同時(shí)會(huì)標(biāo)記輸入的字符串,表明哪些字符已經(jīng)被處理過(guò)了。因此,每次詞法分析器都會(huì)根據(jù)上次設(shè)置的內(nèi)部狀態(tài)和上次處理之后的字符串來(lái)生成一個(gè)新的詞語(yǔ)。 “nextToken” 函數(shù)內(nèi)部使用了超過(guò) 70 種狀態(tài),圖中只顯示了 3 種狀態(tài)。對(duì)于每個(gè)不同的狀態(tài),都有相應(yīng)的處理邏輯。
1.2.3 XSSAuditor 驗(yàn)證詞語(yǔ)當(dāng)詞語(yǔ)生成之后,WebKit 需要使用 XSSAuditor 來(lái)驗(yàn)證詞語(yǔ)流(Token Stream)。XSS 指的是 Cross Site Security , 主要是針對(duì)安全方面的考慮。
根據(jù) XSS 的安全機(jī)制,對(duì)于解析出來(lái)的這些詞語(yǔ),可能會(huì)阻礙某些內(nèi)容的進(jìn)一步執(zhí)行,所以 XSSAuditor 類(lèi)主要負(fù)責(zé)過(guò)濾這些被阻止的內(nèi)容,只有通過(guò)的詞語(yǔ)才會(huì)作后面的處理。
1.2.4 詞語(yǔ)到節(jié)點(diǎn)經(jīng)過(guò)詞法分析器解釋之后的詞語(yǔ)隨之被 XSSAuditor 過(guò)濾并且在沒(méi)有被阻止之后,將被 WebKit 用來(lái)構(gòu)建 DOM 節(jié)點(diǎn)。從詞語(yǔ)到構(gòu)建節(jié)點(diǎn)的步驟是由 HTMLDocumentParser 類(lèi)調(diào)用 HTMLTreeBuilder 類(lèi)的 “constructTree” 函數(shù)來(lái)實(shí)現(xiàn)。
1.2.5 節(jié)點(diǎn)到 DOM 樹(shù)從節(jié)點(diǎn)到構(gòu)建 DOM 樹(shù),包括為樹(shù)中的元素節(jié)點(diǎn)創(chuàng)建屬性節(jié)點(diǎn)等工作由 HTMLConstructionSite 類(lèi)來(lái)完成。正如前面介紹的,該類(lèi)包含一個(gè) DOM 樹(shù)的根節(jié)點(diǎn) ——HTMLDocument 對(duì)象,其他的元素節(jié)點(diǎn)都是它的后代。
因?yàn)?HTML 文檔的 Tag 標(biāo)簽是有開(kāi)始和結(jié)束標(biāo)記的,所以構(gòu)建這一過(guò)程可以使用棧結(jié)構(gòu)來(lái)幫忙。HTMLConstructionSite 類(lèi)中包含一個(gè) “HTMLElementStack” 變量,它是一個(gè)保存元素節(jié)點(diǎn)的棧,其中的元素節(jié)點(diǎn)是當(dāng)前有開(kāi)始標(biāo)記但是還沒(méi)有結(jié)束標(biāo)記的元素節(jié)點(diǎn)。想象一下 HTML 文檔的特點(diǎn),例如一個(gè)片段 “
同 DOM 標(biāo)準(zhǔn)一樣,一切的基礎(chǔ)都是 Node 類(lèi)。在 WebKit 中, DOM 中的接口 Interface 對(duì)應(yīng)于 C++ 的類(lèi),Node 類(lèi)是其他類(lèi)的基類(lèi),圖 5-10 顯示了 DOM 的主要相關(guān)節(jié)點(diǎn)類(lèi)。圖中的 Node 類(lèi)實(shí)際上繼承自 EventTarget 類(lèi),它表明 Node 類(lèi)能夠接受事件,這個(gè)會(huì)在 DOM 事件處理中介紹。Node 類(lèi)還繼承自另外一個(gè)基類(lèi) ——ScriptWrappable,這個(gè)跟 JavaScript 引擎相關(guān)。
Node 的子類(lèi)就是 DOM 中定義的同名接口,元素類(lèi),文檔類(lèi)和屬性類(lèi)均繼承自一個(gè)抽象出來(lái)的 ContainerNode 類(lèi),表明它們能夠包含其他的節(jié)點(diǎn)對(duì)象?;氐?HTML 文檔來(lái)說(shuō),元素和文檔對(duì)應(yīng)的類(lèi)注是 HTMLElement 類(lèi)和 HTMLDocument 類(lèi),實(shí)際上 HTML 規(guī)范還包含眾多的 HTMLElement 子類(lèi),用于表示 HTML 語(yǔ)法中眾多的標(biāo)簽。
1.2.6 網(wǎng)頁(yè)基礎(chǔ)設(shè)施上面介紹了 Frame 、Document 等 WebKit 中的基礎(chǔ)類(lèi),這些都是網(wǎng)頁(yè)內(nèi)部的概念,實(shí)際上,WebKit 提供了更高層次的設(shè)施,用于表示整個(gè)網(wǎng)頁(yè)的一些類(lèi),WebKit 中的 接口部分 就是基于它們來(lái)提供的,表示網(wǎng)頁(yè)的類(lèi)既提供了構(gòu)建 DOM 樹(shù)等操作,同時(shí)也提供了接口用于布局。渲染等操作。
1.2.7 線程化的解釋器在 Renderer 進(jìn)程中有一個(gè)線程,該線程用來(lái)處理 HTML 文檔的解釋任務(wù),在 HTML 解釋器的步驟中,WebKit 的 Chromium 移植跟其他的 WebKit 移植也存在不同之處。
線程化的解釋器就是利用多帶帶的線程來(lái)解釋 HTML 文檔。因?yàn)樵赪ebKit 中,網(wǎng)絡(luò)資源的字節(jié)流自 IO 線程傳遞給渲染線程之后,后面的解釋、布局和渲染等工作基本上就是工作在該線程,也就是渲染線程完成的(這不是絕對(duì)的)。因?yàn)?DOM 樹(shù)只能在渲染線程上創(chuàng)建和訪問(wèn),這也就是說(shuō)構(gòu)建 DOM 樹(shù)的過(guò)程只能在渲染線程中進(jìn)行。但是,從字符到詞語(yǔ)這個(gè)階段可以交給多帶帶的線程來(lái)做,Chromium 瀏覽器使用的就是這個(gè)思想。
具體的實(shí)現(xiàn)過(guò)程:
字符串 (傳給)=> HTMLDocumentParser類(lèi) (創(chuàng)建一個(gè)新的對(duì)象)=> BackgroundHTMLParser 來(lái)負(fù)責(zé)處理 (交給)=> 前一步創(chuàng)建的對(duì)象
WebKit 會(huì)檢查是否需要?jiǎng)?chuàng)建用于解釋字符串的線程 HTMLParserThread 。如果該線程已存在,WebKit 就將剛剛的任務(wù)傳遞給這一新線程, 圖 5-13 描述了這一過(guò)程。
在 HTMLParserThread 線程中,WebKit 所做的事情包括將字符串解釋成一個(gè)個(gè)詞語(yǔ),然后使用之前提到的 XSSAuditor 進(jìn)行安全檢查。這是在一個(gè)新的線程中執(zhí)行。主要區(qū)別在于解釋成詞語(yǔ)之后,WebKit 會(huì)分批次地將結(jié)果詞語(yǔ)傳遞給渲染線程。
1.2.8 JavaScript 的執(zhí)行在 HTML 解釋器的工作過(guò)程中,可能會(huì)有 JavaScript 代碼(全局作用域的代碼)需要執(zhí)行,它發(fā)生在將字符串解釋成詞語(yǔ)之后、創(chuàng)建各種節(jié)點(diǎn)的時(shí)候。這也是全局執(zhí)行的 JavaScript 代碼不能訪問(wèn) DOM 樹(shù)的原因——因?yàn)?DOM 樹(shù)還沒(méi)有被創(chuàng)建完。
所以建議 JavaScript 的使用如下:
1、將 “script” 元素加上 “async” 屬性,表明這是一個(gè)可以異步執(zhí)行的 JavaScript 代碼。
2、將 “script” 元素放在 “body” 元素的最后,這樣它不會(huì)阻礙其他資源的并發(fā)下載。
但是不這樣做的時(shí)候,WebKit 使用預(yù)掃描和預(yù)加載機(jī)制來(lái)實(shí)現(xiàn)資源的并發(fā)下載而不被 JavaScript 的執(zhí)行所阻礙。
具體做法是:當(dāng)遇到需要執(zhí)行 JavaScript 代碼的時(shí)候,WebKit 先暫停當(dāng)前 JavaScript 代碼的執(zhí)行,使用預(yù)先掃描器 HTMLPreloadScanner 類(lèi)來(lái)掃描后面的詞語(yǔ)。如果 WebKit 發(fā)現(xiàn)它們需要使用其他資源,那么使用預(yù)資源加載器 HTMLPreloadScanner 類(lèi)來(lái)發(fā)送請(qǐng)求,在這之后,才執(zhí)行 JavaScript代碼。預(yù)先掃描器本身并不創(chuàng)建節(jié)點(diǎn)對(duì)象,也不會(huì)構(gòu)建 DOM 樹(shù),所以速度比較快。
當(dāng) DOM 樹(shù)構(gòu)建完之后,WebKit 觸發(fā) “DOMContentLoaded” 事件,注冊(cè)在該事件上的 JavaScript 函數(shù)會(huì)被調(diào)用。當(dāng)所在資源都被加載完之后,WebKit 觸發(fā) “onload” 事件。
WebKit 將 DOM 樹(shù)創(chuàng)建過(guò)程中需要執(zhí)行的 JavaScript 代碼交由 HTMLScriptRunner 類(lèi)來(lái)負(fù)責(zé)。工作方式很簡(jiǎn)單,就是利用 JavaScript 引擎來(lái)執(zhí)行 Node 節(jié)點(diǎn)中包含的代碼,具體可以參考 “HTMLScriptRunner::executeParsingBlockingScript” 方法。
1.3 DOM 事件機(jī)制 1.3.1 事件的工作過(guò)程事件在工作過(guò)程中使用兩個(gè)主體,第一個(gè)是事件(event),第二個(gè)是事件目標(biāo)(EventTarget)。WebKit 中用 EventTarget 類(lèi)來(lái)表示 DOM 規(guī)范中 Events 部分定義的事件目標(biāo)。
每個(gè) 事件都有屬性來(lái)標(biāo)記該事件的事件目標(biāo)。當(dāng)事件到達(dá)事件目標(biāo)(如一個(gè)元素節(jié)點(diǎn))的時(shí)候,在這個(gè)目標(biāo)上注冊(cè)的監(jiān)聽(tīng)者(Event Listeners)都會(huì)有觸發(fā)調(diào)用,而這些監(jiān)聽(tīng)者的調(diào)用順序不是固定的,所以不能依賴監(jiān)聽(tīng)者注冊(cè)的順序來(lái)決定你的代碼邏輯。
圖 5-17 是 EventTarget 接口的定義。圖中的接口是用來(lái)注冊(cè)和移除監(jiān)聽(tīng)者的。
事件處理最重要就是事件捕獲(Event capture)和事件冒泡(Event bubbling)這兩種機(jī)制。圖 5-18 是事件捕獲和事件冒泡的過(guò)程。
當(dāng)渲染引擎接收到一個(gè)事件的時(shí)候,它會(huì)通過(guò) HitTest(WebKit 中的一種檢查觸發(fā)gkwrd哪個(gè)區(qū)域的算法)檢查哪個(gè)元素是直接的事件目標(biāo)。在圖 5-18 中,以 “img” 為例,假設(shè)它是事件的直接目標(biāo),這樣,事件會(huì)經(jīng)過(guò)自頂向下和自底向上的兩個(gè)過(guò)程。
事件的捕獲是自頂向下,事件先是到 document 節(jié)點(diǎn),然后一路到達(dá)目標(biāo)節(jié)點(diǎn)。在圖 5-18 中,順序就是 “#document” -> "HTML" -> "body" -> "img" 這樣一個(gè)順序。事件可以在這一傳遞過(guò)程中被捕獲,只需要在注冊(cè)監(jiān)聽(tīng)者的時(shí)候設(shè)置相應(yīng)參數(shù)即可。默認(rèn)情況下,其他節(jié)點(diǎn)不捕獲這樣的事件。如果網(wǎng)頁(yè)注冊(cè)了這樣的監(jiān)聽(tīng)者,那么監(jiān)聽(tīng)者的回調(diào)函數(shù)會(huì)被調(diào)用,函數(shù)可以通過(guò)事件的 “stopPropagation” 函數(shù)來(lái)阻止事件向下傳遞。
事件的冒泡過(guò)程是從下向上的順序,它的默認(rèn)行為是不冒泡,但是是事件包含一個(gè)是否冒泡的屬性。當(dāng)這一屬性為真的時(shí)候,渲染引擎會(huì)將該事件首先傳遞給事件的目標(biāo)節(jié)點(diǎn)的父親,然后是父親的父親,以此類(lèi)推。同捕獲動(dòng)作一樣,這此監(jiān)聽(tīng)函數(shù)也可以使用 “stopPropagation” 函數(shù)來(lái)阻止事件向上傳遞。
1.3.2 WebKit 的事件處理機(jī)制DOM 的事件分為很多種,與用戶相關(guān)的只是其中的一種,稱為 UIEvent ,其他的包括 CustomEvent、MutationEvent 等。UIEvent 又可以分為很多種,包括但是不限于 FocusEvent、MouseEvent、KeyboardEvent、Composition 等。
基于 WebKit 的瀏覽器事件處理過(guò)程,首先是做 HitTest ,查找事件發(fā)生處的元素,檢查該元素有無(wú)監(jiān)聽(tīng)者。如果網(wǎng)頁(yè)的相關(guān)節(jié)點(diǎn)注冊(cè)了事件的監(jiān)聽(tīng)者,那么瀏覽器會(huì)把事件派發(fā)給 WebKit 內(nèi)核來(lái)處理。同時(shí),瀏覽器也可能需要理解和處理這樣的事件。這主要是因?yàn)?,有些事件瀏覽器必須響應(yīng)從而對(duì)網(wǎng)頁(yè)作默認(rèn)處理。
EventHandler 類(lèi)是處理事件的核心類(lèi),它除了需要將各種事件傳給 JavaScript 引擎以調(diào)用響應(yīng)的監(jiān)聽(tīng)者之外,它還會(huì)識(shí)別鼠標(biāo)事件,來(lái)觸發(fā)調(diào)用右鍵菜單、拖放效果等與事件密切相關(guān)的工作,而且 EventHandler 類(lèi)還支持網(wǎng)頁(yè)的多框結(jié)構(gòu)。EventHandler 類(lèi)的接口比較容易理解,但是它的處理邏輯極其復(fù)雜。
圖 5-20 簡(jiǎn)單描述了鼠標(biāo)事件的調(diào)用過(guò)程,這一過(guò)程本身是比較簡(jiǎn)單的,復(fù)雜之處在于 WebKit 的 EventHandler 類(lèi)。
WebKit 中還有些跟事件處理相關(guān)的其他類(lèi),例如 EventPathWalker、EventDispatcher 類(lèi)等,這些類(lèi)都是為了解決事件在 DOM 樹(shù)中傳遞的問(wèn)題。
1.4 影子(Shadow)DOM影子 DOM 是一個(gè)新東西,主要解決了一個(gè)文檔中可能需要大量交互的多個(gè) DOM 樹(shù)建立和維護(hù)各自的功能邊界的問(wèn)題。
1.4.1 什么是影子 DOM當(dāng)開(kāi)發(fā)這樣一個(gè)用戶界面的控件——這個(gè)控件可能由一些 HTML 的標(biāo)簽元素組成,這些元素可以組成一顆 DOM 樹(shù)的子樹(shù)。這樣一個(gè) HTML 控件可以被到處使用,但是問(wèn)題隨之而來(lái),那就是每個(gè)使用控件的地方都會(huì)知道這個(gè)子樹(shù)的結(jié)構(gòu)。
當(dāng)網(wǎng)頁(yè)的開(kāi)發(fā)者需要訪問(wèn)網(wǎng)頁(yè) DOM 樹(shù)的時(shí)候,這些控件內(nèi)部的 DOM 子樹(shù)都會(huì)暴露出來(lái),這些暴露的節(jié)點(diǎn)不僅可能給 DOM 樹(shù)的遍歷帶來(lái)很多麻煩,而且也可能給 CSS 的樣式選擇帶來(lái)問(wèn)題,因?yàn)檫x擇器無(wú)意中可能會(huì)改變這些內(nèi)部節(jié)點(diǎn)的樣式,從而導(dǎo)致很奇怪的控件界面。
如何將內(nèi)部的節(jié)點(diǎn)信息封裝起來(lái),就像 C++ 語(yǔ)言的類(lèi)一樣,同時(shí)又能夠?qū)⑦@些節(jié)點(diǎn)渲染出來(lái)呢 ? W3C 工作組提出的影子 DOM 概念。影子 DOM 的規(guī)范草案能夠使得一些 DOM 節(jié)點(diǎn)在特定范圍內(nèi)可見(jiàn),而在網(wǎng)頁(yè)的 DOM 樹(shù)中卻不可見(jiàn),但是網(wǎng)頁(yè)渲染的結(jié)果中包含了這些節(jié)點(diǎn),這就使得封裝變得容易很多。
圖 5-21 描述了 HTML 文檔對(duì)應(yīng)的 DOM 樹(shù)和 “div” 元素包含的一個(gè)影子 DOM 子樹(shù)。當(dāng)使用 JavaScript 代碼訪問(wèn) HTML 文檔的 DOM 樹(shù)的時(shí)候,通常的接口是不能直接訪問(wèn)到影子 DOM 子樹(shù)中的節(jié)點(diǎn)的,JavaScript 代碼只能通過(guò)特殊的接口方式。
HTML5 支持了很多新的特性,例如對(duì)視頻、音頻的支持,讀者會(huì)發(fā)現(xiàn)這些元素其實(shí)是由很復(fù)雜的控制界面組成,這些界面也是使用 HTML 元素編寫(xiě),但是在 DOM 樹(shù)中,你無(wú)法找到相應(yīng)的節(jié)點(diǎn),這其實(shí)也是使用了影子 DOM 的思想。
因?yàn)橛白?DOM 的子樹(shù)在整個(gè)網(wǎng)頁(yè)的 DOM 樹(shù)中不可見(jiàn),那么事件是如何處理的呢 ?事件中需要包含事件目標(biāo),這個(gè)目標(biāo)當(dāng)然不能是不可見(jiàn)的 DOM 節(jié)點(diǎn),所以事件目標(biāo)其實(shí)就是包含影子 DOM 子樹(shù)的節(jié)點(diǎn)對(duì)象。事件捕獲的邏輯沒(méi)有發(fā)生變化,在影子 DOM 子樹(shù)內(nèi)也會(huì)繼續(xù)傳遞。當(dāng)影子 DOM 子樹(shù)中的事件向上冒泡的時(shí)候, WebKit 會(huì)同時(shí)向整個(gè)文檔的 DOM 上傳遞該事件,以避免一些很奇怪的行為。
1.4.2 WebKit 的支持WebKit 已經(jīng)支持影子 DOM 的規(guī)范草案,雖然還存在一些問(wèn)題。支持影子 DOM 的相關(guān)類(lèi)在目錄 “Source/core/dom/shadow” 下,里面的主要類(lèi)是 ShadowRoot ,表示的是影子 DOM 的根節(jié)點(diǎn)。ShadowRoot 類(lèi)繼承自 DocumentFragment 類(lèi),所以它同樣有 Node 節(jié)點(diǎn)的屬性和方法,因而在影子 DOM 樹(shù)的內(nèi)部,遍歷樹(shù)沒(méi)有什么特別不同的地方。
當(dāng)遍歷 HTML 文檔對(duì)應(yīng) DOM 樹(shù)的時(shí)候,WebKit 需要做特別的判斷,所以讀者會(huì)發(fā)現(xiàn)在 WebKit 的 Node 類(lèi)實(shí)現(xiàn)中存在大量的條件語(yǔ)句,用來(lái)檢查當(dāng)前節(jié)點(diǎn)是否是 ShadowRoot 對(duì)象,如果是該類(lèi)的對(duì)象,把它作為不同 DOM 樹(shù)之間的邊界。有時(shí)候 WebKit 還需要對(duì) ShadowRoot 對(duì)象作出特別處理,比如某些情況會(huì)略過(guò)它的子樹(shù),同樣的,在事件處理的支持類(lèi) EventPathWalker 和 EventRetargeter 中,也需要做一些特別的處理邏輯,原理就是上面所述,細(xì)節(jié)不再介紹。
1.4.3 實(shí)踐:使用影子 DOM示例代碼 5-2 給出了一個(gè)簡(jiǎn)單的使用 webkitCreateShadowRoot 接口來(lái)創(chuàng)建影子 DOM 子樹(shù)的例子。網(wǎng)頁(yè)只包含了一個(gè) “div” 元素,JavaScript 代碼使用該元素創(chuàng)建了一個(gè)影子 DOM 子樹(shù)的根節(jié)點(diǎn),然后該根節(jié)點(diǎn)下加入了兩個(gè)子女,第一個(gè)是圖片元素,第二個(gè)是 “div” 元素,該元素內(nèi)部包含了一些文本。
讀者可以打開(kāi) Chrom 瀏覽器的開(kāi)發(fā)者工具,然后打開(kāi)控制臺(tái),在其中輸入 “document.firstChild.firstChild.nextElementSibling.firstElementChild.firstElementChild” 后會(huì)發(fā)現(xiàn)結(jié)果是空的,根據(jù)對(duì)應(yīng)關(guān)系 “#document-> html -> head -> body -> div -> null”,雖然網(wǎng)頁(yè)中沒(méi)有 ‘head’ 元素,但是 DOM 樹(shù)仍然會(huì)創(chuàng)建該節(jié)點(diǎn)。同時(shí)讀者會(huì)發(fā)現(xiàn) “div” 元素沒(méi)有子女,影子 DOM 子樹(shù)真的被隱藏起來(lái)了,成為真正的影子。
最后希望本文對(duì)你有點(diǎn)幫助。
下期分享 第六章 CSS 解釋器和樣式布局 敬請(qǐng)期待。
對(duì) 全棧開(kāi)發(fā) 有興趣的朋友可以掃下方二維碼關(guān)注我的公眾號(hào) —— 愛(ài)寫(xiě)bugger的阿拉斯加
分享 web 開(kāi)發(fā)相關(guān)的技術(shù)文章,熱點(diǎn)資源,全棧程序員的成長(zhǎng)之路。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/54597.html
摘要:文章同步到技術(shù)內(nèi)幕之頁(yè)面渲染過(guò)程最近拜讀了傳說(shuō)中的技術(shù)內(nèi)幕一書(shū),有很大收獲,尤其是對(duì)頁(yè)面渲染有了較深的認(rèn)識(shí)。解析語(yǔ)法分析,基于詞法解釋器生成的新標(biāo)記,構(gòu)建成抽象語(yǔ)法樹(shù),解析器嘗試將其與某條語(yǔ)法規(guī)則進(jìn)行匹配。 文章同步到github《Webkit技術(shù)內(nèi)幕》之頁(yè)面渲染過(guò)程 最近拜讀了傳說(shuō)中的《Webkit技術(shù)內(nèi)幕》一書(shū),有很大收獲,尤其是對(duì)頁(yè)面渲染有了較深的認(rèn)識(shí)。由于功力有限,而且書(shū)中設(shè)...
摘要:文章同步到技術(shù)內(nèi)幕之頁(yè)面渲染過(guò)程最近拜讀了傳說(shuō)中的技術(shù)內(nèi)幕一書(shū),有很大收獲,尤其是對(duì)頁(yè)面渲染有了較深的認(rèn)識(shí)。解析語(yǔ)法分析,基于詞法解釋器生成的新標(biāo)記,構(gòu)建成抽象語(yǔ)法樹(shù),解析器嘗試將其與某條語(yǔ)法規(guī)則進(jìn)行匹配。 文章同步到github《Webkit技術(shù)內(nèi)幕》之頁(yè)面渲染過(guò)程 最近拜讀了傳說(shuō)中的《Webkit技術(shù)內(nèi)幕》一書(shū),有很大收獲,尤其是對(duì)頁(yè)面渲染有了較深的認(rèn)識(shí)。由于功力有限,而且書(shū)中設(shè)...
摘要:文章同步到技術(shù)內(nèi)幕之頁(yè)面渲染過(guò)程最近拜讀了傳說(shuō)中的技術(shù)內(nèi)幕一書(shū),有很大收獲,尤其是對(duì)頁(yè)面渲染有了較深的認(rèn)識(shí)。解析語(yǔ)法分析,基于詞法解釋器生成的新標(biāo)記,構(gòu)建成抽象語(yǔ)法樹(shù),解析器嘗試將其與某條語(yǔ)法規(guī)則進(jìn)行匹配。 文章同步到github《Webkit技術(shù)內(nèi)幕》之頁(yè)面渲染過(guò)程 最近拜讀了傳說(shuō)中的《Webkit技術(shù)內(nèi)幕》一書(shū),有很大收獲,尤其是對(duì)頁(yè)面渲染有了較深的認(rèn)識(shí)。由于功力有限,而且書(shū)中設(shè)...
摘要:書(shū)接上文瀏覽器內(nèi)核之解釋器和模型本文剖析的解釋器和樣式布局。根據(jù)生成解釋器類(lèi)。而后將解釋后的信息設(shè)置到元素的屬性的樣式中,然后設(shè)置標(biāo)記表明該元素需要重新計(jì)算樣式,并觸發(fā)重新計(jì)算布局。 showImg(https://segmentfault.com/img/remote/1460000016215814); 微信公眾號(hào):愛(ài)寫(xiě)bugger的阿拉斯加如有問(wèn)題或建議,請(qǐng)后臺(tái)留言,我會(huì)盡力解決...
閱讀 2857·2021-11-24 09:39
閱讀 2838·2021-09-23 11:45
閱讀 3453·2019-08-30 12:49
閱讀 3428·2019-08-30 11:18
閱讀 2105·2019-08-29 16:42
閱讀 3398·2019-08-29 16:35
閱讀 1385·2019-08-29 11:21
閱讀 1984·2019-08-26 13:49