009 Node 類型

DOM 是也叫文檔對象模型,是 HTML 和 XML 文檔的一個(gè) API肋乍,其描述了一個(gè)層次節(jié)點(diǎn)樹,允許開發(fā)人員對文檔樹進(jìn)行操作敷存。

Node 接口

DOM 樹是由一個(gè)一個(gè)的節(jié)點(diǎn)構(gòu)成的墓造,DOM1 級中定義了一個(gè) Node 接口,這個(gè)接口會(huì)由頁面中的一個(gè)個(gè)節(jié)點(diǎn)去實(shí)現(xiàn)锚烦。
在 JavaScript 中觅闽,Node 是一個(gè)內(nèi)置對象:

Node
// ? Node() { [native code] }

Node 節(jié)點(diǎn)共分為 12 種類型,每一種類型都由 Node 對象上的一些常量來表示:

Node.ELEMENT_NODE //1
Node.ATTRIBUTE_NODE //2
Node.TEXT_NODE //3
Node.CDATA_SECTION_NODE //4
Node.ENTITY_REFERENCE_NODE //5
Node.ENTITY_NODE //6
Node.PROCESSING_INSTRUCTION_NODE //7
Node.COMMENT_NODE //8
Node.DOCUMENT_NODE //9
Node.DOCUMENT_TYPE_NODE //10
Node.DOCUMENT_FRAGMENT_NODE //11
Node.NOTATION_NODE //12

JavaScript 中每個(gè)節(jié)點(diǎn)對象都有一個(gè) nodeType 屬性挽牢,該屬性的值對應(yīng)著上面的 12 個(gè)常量:

document.nodeType //9
document.body.nodeType //1

注:由于 IE 中的所有 DOM 對象都是以 COM 對象呈現(xiàn)的谱煤,并且 IE 沒有公開 Node 對象的構(gòu)造函數(shù),因此在 IE 中使用 Node 對象上的節(jié)點(diǎn)類型常量可能會(huì)發(fā)生錯(cuò)誤禽拔,因此最好使用節(jié)點(diǎn)對象的 nodeType 屬性,以保證兼容性。
注(2017-08-17):我測試了一下仇味,在 IE9 以下訪問 Node 構(gòu)造函數(shù)會(huì)拋出錯(cuò)誤,在 IE9 以上可以正常使用 Node 的構(gòu)造函數(shù)茧痕。

nodeName 和 nodeValue

節(jié)點(diǎn)的 nodeName 屬性表示元素的標(biāo)簽名,nodeValue 屬性表示元素的節(jié)點(diǎn)值恼除。對于元素節(jié)點(diǎn) nodeValue 屬性將返回 null踪旷。

document.body.nodeName // "BODY"
document.body.nodeValue // null

childNodes

每個(gè)節(jié)點(diǎn)都有一個(gè) childNodes 屬性,里面保存了一個(gè) NodeList 對象豁辉,該對象是一個(gè)類數(shù)組對象令野,用來保存一組有序節(jié)點(diǎn)。
該對象是一個(gè)動(dòng)態(tài)查詢的結(jié)果徽级,因此節(jié)點(diǎn)的變化將會(huì)自動(dòng)在 NodeList 對象中更新气破。該對象有個(gè) length 屬性,表示對象中保存的節(jié)點(diǎn)數(shù)量餐抢,同樣现使,每當(dāng)節(jié)點(diǎn)發(fā)生變化時(shí),該屬性也會(huì)自動(dòng)變化旷痕。

let div = document.createElement("div")
let nodeLists = document.body.childNodes //[]
document.body.appendChild(div)
nodeLists // [div]
nodeList.length // 1
document.body.removeChild(div)
nodeList // [div]
nodeList.length // 1

上面的結(jié)果表明:節(jié)點(diǎn) childNodes 屬性中保存的 NodeLists 對象是隨節(jié)點(diǎn)的變化動(dòng)態(tài)更新的碳锈。
獲取 NodeLists 中的節(jié)點(diǎn)可以使用 [] 語法或者 item 方法:

const node1 = nodeLists[1]
const node2 = nodeLists.item(1)

firstChild 和 lastChild

firstChildlastChild 分別指向 NodeLists 中的第一個(gè)和最后一個(gè)節(jié)點(diǎn),其值分別對應(yīng)于 NodeLists[0] 以及 NodeLists[NodeLists.length - 1]欺抗。
注:firstChildlastChild 是父節(jié)點(diǎn)的屬性售碳,而非 NodeLists 集合的屬性。

const body = document.body
body.firstChild === body.childNodes[0] // true
...

perviousSibling 和 nextSibling

每個(gè)節(jié)點(diǎn)都有一個(gè) previousSiblingnextSibling 屬性佩迟,分別指向其前一個(gè)和后一個(gè)兄弟節(jié)點(diǎn)团滥。如果元素沒有前一個(gè)或后一個(gè)兄弟節(jié)點(diǎn)竿屹,那么其 previousSiblingnextSibling 的值為 null报强。

const body = document.body
body.previousSibling // <head>...</head>
body.nextSibling // null

hasChildNodes 和 ownerDocument

每個(gè)節(jié)點(diǎn)都一個(gè) hasChildNodes() 方法,用來檢驗(yàn)該節(jié)點(diǎn)是否有子節(jié)點(diǎn)(NodeLists 的長度是否大于 1).

const body = document.body
body.hasChildNodes() // true

同時(shí)拱燃,每個(gè)節(jié)點(diǎn)都擁有一個(gè) ownerDocument 屬性秉溉,該屬性執(zhí)行表示該文檔的節(jié)點(diǎn)(document),這個(gè)屬性的意義表示任何節(jié)點(diǎn)都屬于其所在的文檔碗誉。

const body = document.body
body.ownerDocument === document // true

操作節(jié)點(diǎn)

1.appendChild
該方法用來想父元素的 NodeLists 集合中追加元素召嘶,接受一個(gè) Node 對象作為參數(shù)。調(diào)用該方法返回被追加的節(jié)點(diǎn)哮缺。

const firstDiv = docuemnt.createElement("div")
const body = document.body
body.appendChild( firstDiv )
body.childNodes // [div]

另外弄跌,由于相同的節(jié)點(diǎn)不能出現(xiàn)在 DOM 樹的多個(gè)位置中,因此這里如果重復(fù)追加 firstDiv尝苇,NodeLists 中的子節(jié)點(diǎn)數(shù)目不變:

body.appendChild( firstDiv )
body.appendChild( firstDiv )
body.appendChild( firstDiv )
body.childNodes // [div]

同時(shí)铛只,如果將已存在的子節(jié)點(diǎn)追加到 NodeLists 中埠胖,其將會(huì)被挪動(dòng)到 NodeLists 集合的最后一項(xiàng),其之后的節(jié)點(diǎn)將會(huì)頂替該節(jié)點(diǎn)先前的位置淳玩,這個(gè)過程同樣遵循“同一個(gè)節(jié)點(diǎn)不能出現(xiàn) DOM 樹的多個(gè)位置中”這一原則直撤。

如果傳入到 appendChild()中的節(jié)點(diǎn)已經(jīng)是文檔的一部分了,那結(jié)果就是將該節(jié)點(diǎn)從原來的位置
轉(zhuǎn)移到新位置蜕着。即使可以將 DOM 樹看成是由一系列指針連接起來的谋竖,但任何 DOM 節(jié)點(diǎn)也不能同時(shí)出
現(xiàn)在文檔中的多個(gè)位置上。因此承匣,如果在調(diào)用 appendChild()時(shí)傳入了父節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn)蓖乘,那么
該節(jié)點(diǎn)就會(huì)成為父節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)。

上面引用自 《JavaScript 高級程序設(shè)計(jì)(第三版)》韧骗。
2.insertBefore
appendChild驱敲,該方法同樣也由父節(jié)點(diǎn)調(diào)用,該方法接受兩個(gè)參數(shù):

  • 待插入的節(jié)點(diǎn)
  • 參照節(jié)點(diǎn)

調(diào)用該方法時(shí)宽闲,將會(huì)把帶插入的節(jié)點(diǎn)插入到參照節(jié)點(diǎn)之前众眨,如果該方法的最后一個(gè)參數(shù)為 null,那個(gè)調(diào)用這個(gè)方法的表現(xiàn)和 appendChild 相同容诬,都是將節(jié)點(diǎn)追加到 NodeLists 集合的末尾娩梨。調(diào)用該方法返回被插入的節(jié)點(diǎn)。

const div = document.createElement("div")
const p = document.createElement("p")
const body = document.body
body.appendChild(div)
body.insertBefore(p,div)
body.childNodes // [p, div]

同樣览徒,該方法遵循“同一個(gè)節(jié)點(diǎn)不能出現(xiàn) DOM 樹的多個(gè)位置中”原則:

body.insertBefore(p,null)
body.childNodes // [div, p]

3.replaceChild
該方法用來對節(jié)點(diǎn)進(jìn)行替換狈定,接受兩個(gè)參數(shù):

  • 新節(jié)點(diǎn)
  • 被替換的節(jié)點(diǎn)

調(diào)用這個(gè)方法時(shí),被替換的節(jié)點(diǎn)將被移除习蓬,并由新的節(jié)點(diǎn)代替:

const div = document.createElement("div")
const p = document.createElement("p")
const a = document.createElement("a")
const body = document.body
body.appendChild(div)
body.appendChild(p)
body.childNodes // [div, p]
// 替換節(jié)點(diǎn)
body.replaceChild(a,body.firstChild)
body.childNodes // [a, p]

4.removeChild
如果只想從文檔樹中移除某個(gè)節(jié)點(diǎn)纽什,需要調(diào)用 removeChild 方法,該方法接受需要被移除的節(jié)點(diǎn)作為參數(shù)躲叼,返回被移除的節(jié)點(diǎn):

const div = document.createElement("div")
const p = document.createElement("p")
const body = document.body
body.appendChild(div)
body.appendChild(p)
body.childNodes // [div, p]
// 移除節(jié)點(diǎn)
body.removeChild(p) // p
body.childNodes // [div]

5.cloneNode
該方法用來對節(jié)點(diǎn)進(jìn)行復(fù)制芦缰,接受一個(gè)標(biāo)志參數(shù) true 或者 false,表示是否執(zhí)行深復(fù)制枫慷,當(dāng)執(zhí)行深復(fù)制時(shí)让蕾,會(huì)復(fù)制節(jié)點(diǎn)本身以及其所有的子節(jié)點(diǎn)。當(dāng)進(jìn)行淺復(fù)制時(shí)或听,僅復(fù)制節(jié)點(diǎn)本身探孝。

const div = document.createElement("div")
const p = document.createElement("p")
const body = document.body
body.appendChild(div)
body.appendChild(p)
// 進(jìn)行淺復(fù)制
body_shallowcpy = body.cloneNode()
// 進(jìn)行深復(fù)制
body_deepcpy = body.cloneNode(true)
body_shallowcpy .childNodes // []
body_deepcpy .childNodes // [div, p]

總結(jié)

本文主要介紹了 Node 類型以及操作 DOM 的幾個(gè)方法∮桑總結(jié)一下:

  • Node 是一個(gè)接口顿颅,規(guī)定了 DOM 節(jié)點(diǎn)的一些實(shí)現(xiàn)
  • 所有的節(jié)點(diǎn)都會(huì)實(shí)現(xiàn) Node 接口,每個(gè)節(jié)點(diǎn)都有一個(gè) nodeType 屬性足丢,和 Node 對象中的一些常量一一對應(yīng)粱腻,表示該節(jié)點(diǎn)的類型
  • 由于 IE 的 DOM 不是使用原生 JavaScript 實(shí)現(xiàn)的绍填,因此無法獲取到 Node 對象上的這些常量,因此最好使用數(shù)字值進(jìn)行節(jié)點(diǎn)類型的判斷栖疑,以提高兼容性
  • nodeNamenodeValue 屬性
  • childNodes 是一個(gè)包含了元素所有子節(jié)點(diǎn)的一個(gè)集合讨永,該集合是動(dòng)態(tài)的,DOM 樹中的任何變化都會(huì)反映到這個(gè)集合中
  • firstChildlastChild
  • previousSiblingnextSibling
  • hasChildNodes() 可以用來檢驗(yàn)元素是否有子節(jié)點(diǎn)
  • 每個(gè)節(jié)點(diǎn)元素都擁有一個(gè) ownerDocument 屬性遇革,指向文檔節(jié)點(diǎn)卿闹,該屬性的意義表示任何節(jié)點(diǎn)都屬于其所在的文檔
  • 操作節(jié)點(diǎn)的幾個(gè)方法
  • cloneNode() 方法用來復(fù)制節(jié)點(diǎn),有深復(fù)制和淺復(fù)制之分

使用 JavaScript 操作 DOM 可以讓網(wǎng)頁出現(xiàn)多種多樣的變化萝快,JavaScript 中與 DOM 相關(guān)的方法很多很多锻霎,也是使大多數(shù)前端er 們感到陌生或者似懂非懂的地方(正經(jīng)臉:)),因?yàn)槲覀兤綍r(shí)寫代碼時(shí)大多用的是一些 DOM 操作的封裝揪漩,比如 jQuery 庫旋恼,甚至如果你使用 Vue 或者 React 之類的框架,那操作 DOM 的機(jī)會(huì)更少了奄容。不過學(xué)習(xí)這塊的知識(shí)可以扎實(shí)我們的基礎(chǔ)冰更,雖然不一定有太多的使用機(jī)會(huì),但你可以推測這些框架或者庫是怎么實(shí)現(xiàn)這些 DOM 操作的昂勒,對技術(shù)和思維上的提升會(huì)有不少的幫助蜀细。
還是那句話:如果哪天遺忘了,請隨時(shí)回過頭來看看~

完戈盈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奠衔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子塘娶,更是在濱河造成了極大的恐慌归斤,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刁岸,死亡現(xiàn)場離奇詭異脏里,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)难捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門膝宁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人根吁,你說我怎么就攤上這事『媳危” “怎么了击敌?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拴事。 經(jīng)常有香客問我沃斤,道長圣蝎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任衡瓶,我火速辦了婚禮徘公,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哮针。我一直安慰自己关面,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布十厢。 她就那樣靜靜地躺著等太,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛮放。 梳的紋絲不亂的頭發(fā)上缩抡,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音包颁,去河邊找鬼瞻想。 笑死,一個(gè)胖子當(dāng)著我的面吹牛娩嚼,可吹牛的內(nèi)容都是我干的内边。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼待锈,長吁一口氣:“原來是場噩夢啊……” “哼漠其!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起竿音,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤和屎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后春瞬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柴信,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年宽气,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了随常。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萄涯,死狀恐怖绪氛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涝影,我是刑警寧澤枣察,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響序目,放射性物質(zhì)發(fā)生泄漏臂痕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一猿涨、第九天 我趴在偏房一處隱蔽的房頂上張望握童。 院中可真熱鬧,春花似錦叛赚、人聲如沸澡绩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽英古。三九已至,卻和暖如春昙读,著一層夾襖步出監(jiān)牢的瞬間召调,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工蛮浑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唠叛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓沮稚,卻偏偏與公主長得像艺沼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蕴掏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容