DOM 精通了?請問 Node 和 Element 有何區別?

程式設計三昧發表於2022-02-26

前言

相信我們很多同學都經常會使用到 Node(節點)和 Element(節點)的概念,那麼這兩者到底有何區別,不知道有多少人能夠答得上來這個問題?

今天,我在這裡嘗試著解釋一下 Node 和 Element 的區別。

準備工作

在正式開始介紹 Node 和 Element 區別之前,我們先準備以下程式碼:

<div id="parent">
    This is parent content.
    <div id="child1">This is child1.</div>
    <div id="child2">This is child2.</div>
</div>

下面的絕大多數現象和結論都將藉助這段程式碼的結構來進行展示說明。

getElementById 獲取到的到底是什麼?

document.getElementById() 方法應該是我們最常使用的介面之一,那麼它的返回值到底是 Node 還是 Element?

我們使用以下程式碼驗證一下:

let parentEle = document.getElementById('parent');
parentEle instanceof Node
// true
parentEle instanceof Element
// true
parentEle instanceof HTMLElement
// true

可以看到,document.getElementById() 獲取到的結果既是 Node 也是 Element。

Node、ELement 和 HTMLElement 有什麼關係?

上面的程式碼中為什麼要用 Node、Element 和 HTMLElement 來做型別判斷?它們之間到底有何關係?

看程式碼:

let parentEle = document.getElementById('parent');

parentEle.__proto__
// HTMLDivElement {…}

parentEle.__proto__.__proto__
// HTMLElement {…}

parentEle.__proto__.__proto__.__proto__
// Element {…}

parentEle.__proto__.__proto__.__proto__.__proto__
// Node {…}

parentEle.__proto__.__proto__.__proto__.__proto__.__proto__
// EventTarget {…}

parentEle.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
// {constructor: ƒ, …}

parentEle.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
// null

對於以上輸出結果,我們可以用一張圖更直觀地表示它們之間的關係:

各層級關係

這也就解釋了為什麼 getElementById 獲取到的既是 Node 也是 Element,因為 Element 繼承於 Node

從而也可以得出一個結論:Element 一定是 Node,但 Node 不一定是 Element

所以:Element 可以使用 Node 的所有方法

更直白地觀察 Node 和 Element

雖然得出了上面的結論,也清楚了 Node 和 Element 的關係,但是那只是理論,我們還需要更直白的結果來強化對理論的認知。

image-20220220192550930

NodeList 內容:

  • [0] "\n This is parent content."
  • [2] "\n "
  • [4] "\n "

Element.children 獲取到的只是父元素點下的所有 div,而 Element.childNodes 獲取到的卻是父節點下的所有節點(包含文字內容、元素)。

單個 Node 的界限在哪裡?

從上面例子的 NodeList 內容中,換行符 \n 被當成一個單獨的 Node,由此產生了一個新的疑惑:單個 Node 產生的界限在哪裡?

我們將用到的 HTML 程式碼去掉格式化、合併為一行,修改如下:

<div id="parent">This is parent content.<div id="child1">This is child1.</div><div id="child2">This is child2.</div></div>

輸出結果:

image-20220220194417512

NodeList 中的沒有換行符了,原來之前例子中 NodeList 裡的換行符是因為原始程式碼中, HTML 標籤與標籤、內容與標籤之間換行而產生的

現在就可以回答單個 Node 的界限在哪裡了,兩個方面:

  • 單個的 HTML 標籤算是一個單獨的 Node;
  • 針對非 HTML標籤(比如文字、空格等),從一個 HTML 標籤的起始標籤開始,到碰到的第一個 HTML 標籤為止,如果中間有內容(文字、空格等),那這部分內容算是一個 Node。

再進一步

因為上面的例子中使用的都是塊級元素,那如果使用行內元素會怎樣?

試驗一:

<div id="parent">This is parent content.<span>This is a span.</span><div id="child1">This is child1.</div><div id="child2">This is child2.</div></div>

image-20220220195932524

試驗二:

<body>
    <div id="parent">This is parent content\n.
        <span>This is a span.</span>
        <div id="child1">This is child1.</div><div id="child2">This is child2.</div>
    </div>
</body>

image-20220220200356694

可以看到,即使使用了 span 元素,最後的結果也是符合上面得出的單個 Node 界限結論的。

擴充套件

從以上這麼多例子中,我們可以再擴充套件總結一下:

  • HTML 中的換行只能使用 </br> 標籤,\n 會被直接解析成字串;
  • HTML 程式碼中,標籤與文字之間、標籤和標籤之間的換行都會被如實記錄,反映到獲取結果上就是 \n
  • HTML 程式碼中,標籤與標籤、文字與文字、文字與標籤之間的空格不被如實記錄;
  • node.data 內容中 \n 後面的空格字元數和實際程式碼中格式化空格配置數有關,其實也就是“空格會被如實記錄”。

總結

以上通過幾個例子說明了一下 Node 和 Element 之間的區別,主要結論總結起來就是:

  • document.getElementById() 獲取到的結果既是 Node 也是 Element。
  • Element 一定是 Node,但 Node 不一定是 Element,也可能是文字、空格和換行符。
  • NodeList 裡的換行符是因為原始程式碼中, HTML 標籤與標籤、內容與標籤之間換行而產生的。
  • 單個的 HTML 標籤算是一個單獨的 Node。
  • 針對非 HTML標籤(比如文字、空格等),從一個 HTML 標籤的起始標籤開始,到碰到的第一個 HTML 標籤為止,如果中間由內容(文字、空格等),那這部分內容算是一個 Node。

~

~ 本文完,感謝閱讀!

~

學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!

大家好,我是〖程式設計三昧〗的作者 隱逸王,我的公眾號是『程式設計三昧』,歡迎關注,希望大家多多指教!

相關文章