原生DOM介面挺多的,需要花點時間研究下,不過先把基礎整好,後面框架估計好學點。
1. DOM是啥
1.1 知識回顧
先回顧一下HTML的基本結構
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
</head>
<body>
</body>
</html>
複製程式碼
以上就是最簡單的HTML 5的結構。一般我們會把它處理成一棵樹,一棵節點樹。
以上就是一棵樹,瀏覽器把html渲染成的樹,也就是Document結構。每個框就是一個Element、標題等文字內容是Text。
Document、Element、Text 的祖先都是Node。 以下是MDN的繼承樹
可是在記憶體中,存的不是html樹,是一棵對應html各個節點的物件樹,而且物件樹的節點是與html樹的節點一一對應的。
以上是記憶體中的物件樹。這些物件應該怎麼定義,是由DOM規範規定的。
也可以如下這麼理解
頁面中的節點,根據Element、Text、Document、Comment這些建構函式,構造出物件來,記憶體就理解了。
比如構造div
var div = document.createElement('div')
undefined
div
<div></div> //列印結果
複製程式碼
createElement
就是建構函式。
把DOM的對應和JS基本語法練習起來。
1.2 DOM的真面目
前面的基礎分析完了,就可以知道什麼是DOM了。
DOM就是完整的把Document和Object對映到一起,符合DOM規範的結構,所以具備很多的API。
DOM 是 JavaScript 操作網頁的介面,全稱為“文件物件模型”(Document Object Model)。它的作用是將網頁轉為一個 JavaScript 物件,從而可以用指令碼進行各種操作(比如增刪內容)。
瀏覽器會根據 DOM 模型,將結構化文件(比如 HTML 和 XML)解析成一系列的節點,再由這些節點組成一個樹狀結構(DOM Tree)。所有的節點和最終的樹狀結構,都有規範的對外介面。所以,DOM 可以理解成網頁的程式設計介面。DOM 有自己的國際標準,目前的通用版本是DOM 3,下一代版本DOM 4正在擬定中。
嚴格地說,DOM 不屬於 JavaScript,但是操作 DOM 是 JavaScript 最常見的任務,而 JavaScript 也是最常用於 DOM 操作的語言。
規範裡的DOM模型竟然有多達31個介面,我先只挑Node介面和Document介面學習。
2. 原生DOM API
DOM是一棵樹,樹上有Node,Node分為Document、Element、Text,其他的可以忽略。
由前面的繼承圖可知Node的研究價值很高,我們先來看Node介面的屬性和方法。 下面是MDN對Node介面的解釋
Node是一個介面,許多DOM型別從這個介面繼承,並允許類似地處理(或測試)這些各種型別。
- 以下介面都從Node繼承其方法和屬性: Document, Element, CharacterData (which Text, Comment, and CDATASection inherit), ProcessingInstruction, DocumentFragment, DocumentType, Notation, Entity, EntityReference
2.1 Node的屬性
DOM樹的最小單位就是節點(node)。文件的樹形結構就是有各個不同列型別的節點組成的,每個節點都可以看做是這棵DOM樹的葉子
常見的七種node型別
名字 | 作用 |
---|---|
Document | 整個文件樹的頂層節點 但不是根節點 |
DocumentType | doctype標籤 |
Element | 網頁的其他各種標籤 |
Attribute | 標籤的屬性 |
Text | 標籤與標籤之間的文字 |
Comment | 註釋 |
DocumentFragment | 文件的片段 |
我們只關心Document、Element、Text
2.1.1 node之間的關係屬性
- 一個最頂層的節點 document,代表整個文件。
- 一個根節點 html,是文件裡面最高的一層,是根節點。其他所有的html標籤都是他的下級
- 其他節點與周圍節點的關係
- parentNode: 直接的那個上級的節點
- childNodes: 直接的下一級的節點
- sibling:擁有同一個父節點的節點
Node.childNodes
返回一個NodeList集合,成員包括當前節點的所有子節點。注意,除了HTML元素節點,該屬性返回的還包括Text節點和Comment節點。如果當前節點不包括任何子節點,則返回一個空的NodeList集合。由於NodeList物件是一個動態集合,一旦子節點發生變化,立刻會反映在返回結果之中。
- childNodes
注意: childNodes會把text也列印出來,也就是兩個標籤之間的換行符。
而且childNodes返回一個偽陣列。這個偽陣列物件的每一個元素依然都是html元素,如果想操作元素的內容還要用元素的其他屬性。
- document的childNodes只有兩個
document.childNodes
(2) [<!DOCTYPE html>, html]
0:<!DOCTYPE html>
1:html
length:2
__proto__: NodeList
複製程式碼
- 所以我們不想列印出text節點,可以使用
children
屬性啊
ParentNode.children //ParentNode要是一個HTMLCollection
複製程式碼
上述children屬性會返回 一個Node的子elements 而沒有text節點。
Node.firstChild和Node.lastChild
firstChild
屬性返回當前節點的第一個子節點,如果當前節點沒有子節點,則返回null
(注意,不是undefined
)。
Node.lastChild
屬性返回當前節點的最後一個子節點,如果當前節點沒有子節點,則返回null。
Node.nextSibling和Node.previousSibling
Node.nextSibling
屬性返回緊跟在當前節點後面的第一個同級節點。如果當前節點後面沒有同級節點,則返回null
。
previousSibling
屬性返回當前節點前面的、距離最近的一個同級節點。如果當前節點前面沒有同級節點,則返回null
。
以上的三組屬性使用時一定要注意結果會有text的影響。
Node.parentNode
document.parentNode
null
複製程式碼
parentNode
屬性返回當前節點的父節點。對於一個節點來說,它的父節點只可能是三種型別:element
節點、document
節點和documentfragment
節點。
而且document節點和documentfragment節點,它們的父節點都是null。另外,對於那些生成後還沒插入DOM樹的節點,父節點也是null。
Node.parentElement
parentElement屬性返回當前節點的父Element節點。如果當前節點沒有父節點,或者父節點型別不是Element節點,則返回null。
注意: 在IE瀏覽器中,只有Element節點才有該屬性,其他瀏覽器則是所有型別的節點都有該屬性。
ownerDocument
屬性返回當前節點所在的頂層文件物件,即document
物件
2.1.2 node自身的屬性
nodeName和nodeType
nodeName返回node的名字,如果是element那名字是大寫的,其他的名字前面寫上#。nodeType返回node的型別,一般用數字表示,1表示element(也可以用Node.ELEMENT_NODE來表示),3表示text(Node.TEXT_NODE)。
- 如果是element,那麼nodeName === tagName
- 如果是text,那麼nodeName = #text, tagName = undefined
關於nodeType有個詳細的表格,應該檢視MDN記住。對照表
nodeValue
nodeValue屬性返回或設定當前節點的值。對於text, comment節點來說, nodeValue
返回該節點的文字內容,對於 attribute 節點來說, 返回該屬性的屬性值,而對於document和element節點來說,返回null。
貌似沒個卵用,最常見的element都是null,還是用下面的textContent
吧
textContent和innerText之爭
火狐推出的textContent,獲得一個節點及其後代的文字內容,一般用這個獲得元素的內容。
IE推出的innerText,兩者有很大的區別。
關於textContent、innerText、innnerHTML之間的區別請看MDN
2.2 Node的方法
Node.appendChild()
Node.appendChild()
方法將一個節點新增到指定父節點的子節點列表的末尾。
var child = node.appendChild(child);
node
是要插入子節點的父節點.child
即是引數又是這個方法的返回值.
Node.cloneNode()
這個方法就是克隆一個node,分為淺拷貝和深拷貝。
- 淺拷貝,
Node.cloneNode()
只克隆元素節點本身,而不會克隆它的子節點。包括它的文字節點 - 深拷貝,
Node.cloneNode(true)
克隆元素的所有屬性以及子節點
如果你想克隆同一個id的元素到不同的元素後面,會報錯。注意:為了防止一個文件中出現兩個ID重複的元素,使用cloneNode()方法克隆的節點在需要時應該指定另外一個與原ID值不同的ID
Node.contains()
判斷一個節點是不是另一個節點的子節點。
div.contains(div1)
複製程式碼
div1是div的子節點就返回true。
Node.hasChildNodes()
div.hasChildNodes()
複製程式碼
判斷div節點是否還有子節點,有子節點就返回true。
Node.insertBefore()
在當前節點的某個子節點之前再插入一個子節點。
var insertedElement = parentElement.insertBefore(newElement, referenceElement);
複製程式碼
在parentElement節點的子節點referenceElement前面插入一個newElement節點。
如果referenceElement
為null
則newElement
將被插入到子節點的末尾*。*如果newElement
已經在DOM樹中,newElement
首先會從DOM樹中移除。
沒有 insertAfter
方法。可以使用 insertBefore
方法和 nextSibling
來模擬它。
parentDiv.insertBefore(sp1, sp2.nextSibling);
複製程式碼
只要sp2.nextSibling === null,那麼就可以在parentDiv的末尾新增sp1元素。
Node.isEqualNode()和Node.isSameNode()
兩者都是比較兩個node是否相等。不過isEqualNode()是兩個node看起來相等就返回true,isSameNode()嚴格使用===判斷,而且該方法已被廢棄,如果要嚴格判斷兩個node是否指向同一個物件,直接用node1 === node2。
Node.normalize()
就是規範化的意思。什麼是規範化,在一個"規範化"後的DOM樹中,不存在一個空的文字節點,或者兩個相鄰的文字節點。
var wrapper = document.createElement("div");
wrapper.appendChild(document.createTextNode("Part 1 "));
wrapper.appendChild(document.createTextNode("Part 2 "));
// 這時(規範化之前),wrapper.childNodes.length === 2
// wrapper.childNodes[0].textContent === "Part 1 "
// wrapper.childNodes[1].textContent === "Part 2 "
wrapper.normalize();
// 現在(規範化之後), wrapper.childNodes.length === 1
// wrapper.childNodes[0].textContent === "Part 1 Part 2"
複製程式碼
以上是MDN的例子,很好懂。
以上是自己模仿的demo
Node.removeChild()和Node.replaceChild()
Node.removeChild()是從當前節點刪除一個子節點,不過記憶體裡面依然存在,只不過不在頁面顯示了,返回的就是被移除的那個節點。所以說一個節點移除以後,依然可以使用它,比如插入到另一個節點下面。
以上是我做的demo
Node.replaceChild
方法用於將一個新的節點,替換掉當前節點的一個子節點。它接受兩個引數,第一個引數是用來替換的新節點,第二個引數將要被替換走的子節點。它返回被替換走的那個節點。
我做的實驗demo
Document屬性和方法
關於Document介面的屬性和方法,且聽下回分解~