前言
相信我們很多同學(xué)都經(jīng)常會使用到 Node(節(jié)點)和 Element(節(jié)點)的概念,那么這兩者到底有何區(qū)別,不知道有多少人能夠答得上來這個問題?
今天,我在這里嘗試著解釋一下 Node 和 Element 的區(qū)別。
準(zhǔn)備工作
在正式開始介紹 Node 和 Element 區(qū)別之前,我們先準(zhǔn)備以下代碼:
<div id="parent">
This is parent content.
<div id="child1">This is child1.</div>
<div id="child2">This is child2.</div>
</div>
下面的絕大多數(shù)現(xiàn)象和結(jié)論都將借助這段代碼的結(jié)構(gòu)來進行展示說明。
getElementById 獲取到的到底是什么?
document.getElementById()
方法應(yīng)該是我們最常使用的接口之一,那么它的返回值到底是 Node 還是 Element?
我們使用以下代碼驗證一下:
let parentEle = document.getElementById('parent');
parentEle instanceof Node
// true
parentEle instanceof Element
// true
parentEle instanceof HTMLElement
// true
可以看到,document.getElementById()
獲取到的結(jié)果既是 Node 也是 Element。
Node、ELement 和 HTMLElement 有什么關(guān)系?
上面的代碼中為什么要用 Node、Element 和 HTMLElement 來做類型判斷?它們之間到底有何關(guān)系?
看代碼:
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
對于以上輸出結(jié)果,我們可以用一張圖更直觀地表示它們之間的關(guān)系:
這也就解釋了為什么 getElementById 獲取到的既是 Node 也是 Element,因為 Element 繼承于 Node。
從而也可以得出一個結(jié)論:Element 一定是 Node,但 Node 不一定是 Element。
所以:Element 可以使用 Node 的所有方法。
更直白地觀察 Node 和 Element
雖然得出了上面的結(jié)論,也清楚了 Node 和 Element 的關(guān)系,但是那只是理論,我們還需要更直白的結(jié)果來強化對理論的認(rèn)知。
NodeList 內(nèi)容:
- [0] "\n This is parent content."
- [2] "\n "
- [4] "\n "
Element.children
獲取到的只是父元素點下的所有 div,而 Element.childNodes
獲取到的卻是父節(jié)點下的所有節(jié)點(包含文本內(nèi)容、元素)。
單個 Node 的界限在哪里?
從上面例子的 NodeList 內(nèi)容中,換行符 \n
被當(dāng)成一個單獨的 Node,由此產(chǎn)生了一個新的疑惑:單個 Node 產(chǎn)生的界限在哪里?
我們將用到的 HTML 代碼去掉格式化、合并為一行,修改如下:
<div id="parent">This is parent content.<div id="child1">This is child1.</div><div id="child2">This is child2.</div></div>
輸出結(jié)果:
NodeList 中的沒有換行符了,原來之前例子中 NodeList 里的換行符是因為原始代碼中, HTML 標(biāo)簽與標(biāo)簽、內(nèi)容與標(biāo)簽之間換行而產(chǎn)生的。
現(xiàn)在就可以回答單個 Node 的界限在哪里了,兩個方面:
- 單個的 HTML 標(biāo)簽算是一個單獨的 Node;
- 針對非 HTML標(biāo)簽(比如文本、空格等),從一個 HTML 標(biāo)簽的起始標(biāo)簽開始,到碰到的第一個 HTML 標(biāo)簽為止,如果中間有內(nèi)容(文本、空格等),那這部分內(nèi)容算是一個 Node。
再進一步
因為上面的例子中使用的都是塊級元素,那如果使用行內(nèi)元素會怎樣?
試驗一:
<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>
試驗二:
<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>
可以看到,即使使用了 span 元素,最后的結(jié)果也是符合上面得出的單個 Node 界限結(jié)論的。
擴展
從以上這么多例子中,我們可以再擴展總結(jié)一下:
- HTML 中的換行只能使用
</br>
標(biāo)簽,\n
會被直接解析成字符串; - HTML 代碼中,標(biāo)簽與文本之間、標(biāo)簽和標(biāo)簽之間的換行都會被如實記錄,反映到獲取結(jié)果上就是
\n
; - HTML 代碼中,標(biāo)簽與標(biāo)簽、文本與文本、文本與標(biāo)簽之間的空格不被如實記錄;
-
node.data
內(nèi)容中\n
后面的空格字符數(shù)和實際代碼中格式化空格配置數(shù)有關(guān),其實也就是“空格會被如實記錄”。
總結(jié)
以上通過幾個例子說明了一下 Node 和 Element 之間的區(qū)別,主要結(jié)論總結(jié)起來就是:
-
document.getElementById()
獲取到的結(jié)果既是 Node 也是 Element。 - Element 一定是 Node,但 Node 不一定是 Element,也可能是文本、空格和換行符。
- NodeList 里的換行符是因為原始代碼中, HTML 標(biāo)簽與標(biāo)簽、內(nèi)容與標(biāo)簽之間換行而產(chǎn)生的。
- 單個的 HTML 標(biāo)簽算是一個單獨的 Node。
- 針對非 HTML標(biāo)簽(比如文本、空格等),從一個 HTML 標(biāo)簽的起始標(biāo)簽開始,到碰到的第一個 HTML 標(biāo)簽為止,如果中間由內(nèi)容(文本、空格等),那這部分內(nèi)容算是一個 Node。
~
~ 本文完,感謝閱讀!
~
學(xué)習(xí)有趣的知識,結(jié)識有趣的朋友,塑造有趣的靈魂!