Chrome 開發人員工具新增了對頂層元素的支援,方便開發人員對使用頂層元素的程式碼進行偵錯。
本文將說明頂層元素為何,以及 DevTools 如何協助以視覺化方式呈現頂層內容,以便瞭解及偵錯含有頂層元素的 DOM 結構,並說明如何實作 DevTools 頂層支援功能。
頂層和頂層元素是什麼?
當您以模式視窗開啟 <dialog>
時,內部會發生哪些事?🤔
並放入頂層。頂層內容會在所有其他內容上方顯示。舉例來說,模式對話方塊需要顯示在所有其他 DOM 內容之上,因此瀏覽器會自動在「頂層」中轉譯此元素,而不會強迫作者手動調整 z 索引。頂層元素會顯示在元素上方,即使該元素的 Z-index 值最高也一樣。
頂層可視為「最高堆疊層」。每份文件都會與單一視區相關聯,因此也只有一個頂層。頂層可同時包含多個元素。在這種情況下,這些圖層會彼此堆疊,最後一個圖層會位於最上層。換句話說,所有頂層元素都會放入頂層的後進先出 (LIFO) 堆疊中。
<dialog>
元素並非瀏覽器在頂層轉譯的唯一元素。目前,頂層元素包括:彈出式視窗、模態對話方塊,以及全螢幕模式中的元素。
請查看下列對話方塊實作方式:
<main>
<button onclick="window.dialog.showModal();">Open Dialog</button>
</main>
<dialog id="dialog"></dialog>
以下是示範,其中有幾個對話方塊,其背景已套用樣式 (下文會說明背景):
什麼是背景?
幸好,我們有辦法自訂頂層元素下方的內容。
背景是可視區大小的方塊,會在任何頂層元素下方立即算繪。當 ::backdrop
擬元素是頂層中的最上層時,您可以使用該元素遮蓋、樣式化或完全隱藏位於該元素下方的所有內容。
當您建立多個元素模式時,瀏覽器會在最前方的元素下方和其他全螢幕元素上方,立即繪製背景。
以下說明如何設定背景樣式:
/* The browser displays the backdrop only when the dialog.showModal() function opens the dialog.*/
dialog::backdrop {
background: rgba(255,0,0,.25);
}
如何只顯示第一個背景?
每個頂層元素都有屬於頂層堆疊的背景。這些背景會重疊,因此如果背景的透明度不是 100%,底下的背景就會顯示。
如果只需要顯示頂層堆疊中的首個背景,您可以追蹤頂層堆疊中的項目 ID,以便達成這項目標。
如果新增的元素不是頂層中的第 1 個元素,則在將元素放入頂層時呼叫的函式會將 hiddenBackdrop
類別套用至 ::backdrop
。當元素從頂層移除時,這個類別就會移除。
請查看這個示範範例中的程式碼:
開發人員工具中的頂層支援設計
頂層的 DevTools 支援功能可協助開發人員瞭解頂層的概念,並以視覺化方式呈現頂層內容的變化。這些功能可協助開發人員找出下列項目:
- 頂層元素的任何時間點和順序。
- 堆疊中任何時間點的頂端元素。
此外,DevTools 頂層支援功能有助於在頂層堆疊中以視覺化方式呈現背景假元素的位置。雖然這不是樹狀結構元素,但在頂層運作方式中扮演重要角色,對開發人員也相當實用。
頂層支援功能可讓您:
- 觀察頂層堆疊中隨時的元素。當頂層新增或移除元素時,頂層表示法堆疊會動態變更。
- 查看頂層堆疊中的元素位置。
- 從樹狀結構中的頂層元素或元素背景假元素,跳至頂層表示層容器中的元素或背景假元素,反之亦然。
讓我們來看看如何使用這些功能!
頂層容器
為了方便顯示頂層元素,DevTools 會在元素樹狀結構中新增頂層容器。位於結尾 </html>
標記之後。
這個容器可讓您隨時觀察頂層堆疊中的元素。頂層容器是頂層元素及其背景的連結清單。當頂層新增或移除元素時,頂層表示法堆疊會動態變更。
如要在元素樹狀結構或頂層容器中尋找頂層元素,請按一下頂層容器中頂層元素表示法與元素樹狀結構中相同元素的連結,並反過來。
如要從頂層容器元素跳至頂層樹狀結構元素,請按一下頂層容器中元素旁的「揭露」按鈕。
如要從頂層樹狀結構元素跳至頂層容器中的連結,請按一下元素旁邊的「頂層」徽章。
您可以關閉任何徽章,包括頂層徽章。如要停用徽章,請在任一徽章上按一下滑鼠右鍵,選擇「徽章設定」,然後取消勾選要隱藏的徽章旁邊的勾號。
頂層堆疊中的元素順序
頂層容器會以相反的順序顯示堆疊中的元素。堆疊元素的頂端是頂層容器元素清單中的最後一個。也就是說,頂層容器清單中的最後一個元素,就是您目前可在文件中互動的元素。
樹狀圖元素旁邊的徽章會指出元素是否屬於頂層,並包含元素在堆疊中的編號。
在這個螢幕截圖中,頂層堆疊包含兩個元素,第二個元素位於堆疊頂端。如果移除第二個元素,第一個元素就會移至頂端。
頂層容器中的背景
如前所述,每個頂層元素都有一個名為 backdrop 的 CSS 擬造元素。您可以為這個元素設定樣式,因此檢查並查看其表示方式也很實用。
在元素樹狀圖中,背景元素會位於所屬元素的結尾標記之前。不過,在頂層容器中,背景連結會列在所屬頂層元素的正上方。
DOM 樹狀結構的變更
ElementsTreeElement
是負責在 DevTools 中建立及管理個別 DOM 樹狀結構元素的類別,但無法實作頂層容器。
為了將頂層容器顯示為樹狀結構中的節點,我們新增了一個類別,用於建立 DevTools 樹狀結構元素節點。先前,負責建立 DevTools 元素樹狀結構的類別會使用 DOMNode
初始化每個 TreeElement
,DOMNode
是包含 backendNodeId
和其他後端相關屬性的類別。backendNodeId
則會在後端指派。
頂層容器節點,包含頂層元素的連結清單,用於做為一般樹狀結構元素節點。不過,這個節點並非「實際」的 DOM 節點,後端也不需要建立頂層容器節點。
為了建立代表頂層的前端節點,我們新增了一種不含 DOMNode
的前端節點。這個頂層容器元素是第一個沒有 DOMNode
的前端節點,也就是說,它只存在於前端,後端「不知道」這個元素。為了讓行為與其他節點相同,我們建立了新的 TopLayerContainer
類別,該類別會擴充負責前端節點行為的 UI.TreeOutline.TreeElement
類別。
為了達到所需的刊登位置,會顯示元素的類別會將 TopLayerContainer
附加為 <html>
標記的下一個同胞節點。
新的頂層徽章表示元素位於頂層,並可連結至 TopLayerContainer
元素中此元素的捷徑。
初始設計
一開始,我們原本打算將頂層元素複製到頂層容器,而不是建立元素的連結清單。我們沒有實作這個解決方案,因為元素子項在開發人員工具中擷取的方式。每個元素都有一個用於擷取子項的父項指標,因此無法擁有多個指標。因此,我們無法讓節點正確展開,並在樹狀結構的多個位置包含所有子項。一般來說,系統並未考量重複的子樹。
我們做出的妥協是建立連結至前端 DOM 節點,而非複製這些節點。負責在開發人員工具中建立元素連結的類別是 ShortcutTreeElement
,它會擴充 UI.TreeOutline.TreeElement
。ShortcutTreeElement
的行為與其他 DevTools DOM 樹狀結構元素相同,但在後端沒有對應的節點,且具有連結至 ElementsTreeElement
的按鈕。頂層節點的每個 ShortcutTreeElement
都有一個子 ShortcutTreeElement
,可連結至 DevTools DOM 樹狀結構中的 ::backdrop
擬元素表示法。
初始設計:
Chrome 開發人員工具通訊協定 (CDP) 異動
如要實作頂層支援功能,您必須變更 Chrome 開發人員工具通訊協定 (CDP)。CDP 是 DevTools 和 Chromium 之間的通訊協定。
我們需要新增以下內容:
- 可隨時從前端呼叫的指令。
- 從後端觸發前端的事件。
CDP:DOM.getTopLayerElements
指令
如要顯示目前頂層元素,我們需要新的實驗性 CDP 指令,以便傳回頂層元素的節點 ID 清單。每當開發人員工具開啟或頂層元素變更時,開發人員工具就會呼叫此指令。這個指令如下所示:
# Returns NodeIds of the current top layer elements.
# Top layer renders closest to the user within a viewport, therefore, its elements always
# appear on top of all other content.
experimental command getTopLayerElements
returns
# NodeIds of the top layer elements.
array of NodeId nodeIds
CDP:DOM.topLayerElementsUpdated
事件
為了取得最新的頂層元素清單,我們需要頂層元素的每項變更都觸發實驗性 CDP 事件。此事件會通知前端變更,然後呼叫 DOM.getTopLayerElements
指令並接收新元素清單。
事件如下所示:
# Called by the change of the top layer elements.
experimental event topLayerElementsUpdated
CDP 考量事項
頂層的 CDP 支援功能可透過多種方式實作。我們考慮的另一個做法是建立事件,以便傳回頂層元素清單,而非只告知前端是否新增或移除頂層元素。
或者,我們可以建立兩個事件,而非指令:topLayerElementAdded
和 topLayerElementRemoved
。在這種情況下,我們會收到一個元素,並需要在前端管理頂層元素的陣列。
目前,前端事件會呼叫 getTopLayerElements
指令,取得更新元素的清單。如果我們在每次觸發事件時傳送元素清單或造成變更的特定元素,就可以省略呼叫指令的步驟。不過,在這種情況下,前端就無法控制要推送哪些元素。
我們採用這種做法,是因為我們認為前端決定何時要求頂層節點會比較好。舉例來說,如果 UI 中的頂層已摺疊,或是使用者使用的 DevTools 面板沒有元素樹狀結構,就不需要取得可能位於樹狀結構更深層的額外節點。