DOM之通俗易懂講解

DOM是所有前端開(kāi)發(fā)每天打交道的東西寻拂,但是隨著jQuery等庫(kù)的出現(xiàn)委乌,大大簡(jiǎn)化了DOM操作返十,導(dǎo)致大家慢慢的“遺忘”了它的本來(lái)面貌。不過(guò)吸奴,要

想深入學(xué)習(xí)前端知識(shí),對(duì)DOM的了解是不可或缺的,所以本文力圖系統(tǒng)的講解下DOM的相關(guān)知識(shí)则奥,如有遺漏或錯(cuò)誤考润,還請(qǐng)大家指出一起討論^ ^。

一读处、DOM是什么糊治?

DOM(文檔對(duì)象模型)是針對(duì)HTML和XML文檔的一個(gè)API,通過(guò)DOM可以去改變文檔罚舱。

這個(gè)說(shuō)法很官方井辜,大家肯定還是不明白。

舉個(gè)例子:我們有一段HTML管闷,那么如何訪問(wèn)第二層第一個(gè)節(jié)點(diǎn)呢粥脚,如何把最后一個(gè)節(jié)點(diǎn)移動(dòng)到第一個(gè)節(jié)點(diǎn)上面去呢?

DOM就是定義了如果做類似操作包个,那么應(yīng)該怎么做的標(biāo)準(zhǔn)刷允。比如用getElementById來(lái)訪問(wèn)節(jié)點(diǎn),用insertBefore來(lái)插入節(jié)點(diǎn)碧囊。

當(dāng)瀏覽器載入HTML時(shí)树灶,會(huì)生成相應(yīng)的DOM樹(shù)。

簡(jiǎn)而言之糯而,DOM可以理解為一個(gè)訪問(wèn)或操作HTML各種標(biāo)簽的實(shí)現(xiàn)標(biāo)準(zhǔn)天通。

對(duì)于一個(gè)HTML來(lái)說(shuō),文檔節(jié)點(diǎn)Document(看不到的)是它的根節(jié)點(diǎn)熄驼,對(duì)應(yīng)的對(duì)象便是document對(duì)象(嚴(yán)格講是子類HTMLDocument對(duì)象像寒,下面單獨(dú)介紹Document類型時(shí)會(huì)指出)。

換句話說(shuō)存在一個(gè)文檔節(jié)點(diǎn)Document谜洽,然后它有子節(jié)點(diǎn)萝映,比如通過(guò)document.getElementsByTagName(“html”),得到類型為元素節(jié)點(diǎn)的Element html阐虚。

每一段HTML標(biāo)記都可以用相應(yīng)的節(jié)點(diǎn)表示序臂,例如:

HTML元素通過(guò)元素節(jié)點(diǎn)表示,注釋通過(guò)注釋節(jié)點(diǎn)表示实束,文檔類型通過(guò)文檔類型節(jié)點(diǎn)表示等奥秆。

一共定義了12種節(jié)點(diǎn)類型,而這些類型又都繼承自Node類型咸灿。

所以我們首先講Node類型构订,因?yàn)檫@個(gè)類型的方法是所有節(jié)點(diǎn)都會(huì)繼承的。

二避矢、Node類型(基類悼瘾,所有節(jié)點(diǎn)都繼承了它的方法)

Node是所有節(jié)點(diǎn)的基類型囊榜,所有節(jié)點(diǎn)都繼承自它,所以所有節(jié)點(diǎn)都有一些共同的方法和屬性亥宿。

先講Node類型的屬性

首先是nodeType屬性卸勺,用來(lái)表明節(jié)點(diǎn)類型的,例如:

document.nodeType;? ? // 返回 9 烫扼,其中document對(duì)象為文檔節(jié)點(diǎn)Document的實(shí)例

這里面曙求,9代表的就是DOCUMENT_NODE節(jié)點(diǎn)的意思,可以通過(guò)Node.DOCUMENT_NODE查看節(jié)點(diǎn)對(duì)應(yīng)的數(shù)字

document.nodeType === Node.DOCUMENT_NODE;? ? // true

至于一共有哪些節(jié)點(diǎn)映企,每個(gè)節(jié)點(diǎn)對(duì)應(yīng)的數(shù)字又是多少悟狱,這個(gè)可以問(wèn)谷歌就知道了。反正常用的就是元素節(jié)點(diǎn)Element(對(duì)應(yīng)數(shù)字為1)和文本節(jié)點(diǎn)Text(對(duì)應(yīng)數(shù)字為3)

然后常用的還有nodeName和nodeValue

對(duì)于元素節(jié)點(diǎn) nodeName就是標(biāo)簽名堰氓,nodeValue就是null

對(duì)于文本節(jié)點(diǎn) nodeName為”#text”(chrome里面測(cè)試的),nodeValue就是實(shí)際的值

每個(gè)節(jié)點(diǎn)還有childNodes屬性挤渐,這是個(gè)十分重要的屬性,它保存了這個(gè)節(jié)點(diǎn)所有直接子元素

調(diào)用childNodes返回的是一個(gè)NodeList對(duì)象豆赏,它極其像數(shù)組挣菲,但是有一個(gè)最關(guān)鍵的地方富稻,它是動(dòng)態(tài)查詢的掷邦,也就是說(shuō)每次調(diào)用它都會(huì)對(duì)DOM結(jié)構(gòu)查詢,所以對(duì)它的使用需要慎重椭赋,注意性能抚岗。

訪問(wèn)childNodes可以使用數(shù)組下表或者item方法

然后各個(gè)節(jié)點(diǎn)還存在各種屬性讓它們可以相互訪問(wèn),下圖很好的總結(jié)了


比較有用的方法和屬性:

還有一點(diǎn)哪怔,如果要?jiǎng)討B(tài)寫入腳本 例如 xxx這樣的

宣蔚,那么要注意把分開(kāi)來(lái)拼裝下,否則會(huì)被誤以為是腳本結(jié)束的標(biāo)志认境,導(dǎo)致這個(gè)結(jié)束符匹配到上面一個(gè)開(kāi)始符胚委。可以這樣

寫””;

四叉信、Element類型

接下來(lái)講講最重要也是最常見(jiàn)的一個(gè)類型亩冬,Element類型。

我們?nèi)粘K僮鞯亩际荅lement類型(實(shí)質(zhì)是HTMLElement硼身,這里為了方便理解硅急,就簡(jiǎn)單這么說(shuō)),比如

document.getElementById("test")

返回的就是Element類型佳遂。我們?nèi)粘Kf(shuō)的“DOM對(duì)象”营袜,通常也就是指Element類型的對(duì)象。

然后說(shuō)說(shuō)這個(gè)類型的常見(jiàn)屬性:

首先最開(kāi)始說(shuō)的Node類型上的那些屬性方法它都有丑罪,這個(gè)就不再重復(fù)了荚板,主要說(shuō)說(shuō)它自己獨(dú)有的凤壁。

首先是tagName,這個(gè)和繼承自Node類型的nodeName一樣跪另。都是返回標(biāo)簽名客扎,通常是大寫,結(jié)果取決于瀏覽器罚斗。所以在做比較

的時(shí)候最好是調(diào)用下類似toLowerCase()這種方法再做比較徙鱼。

說(shuō)說(shuō)上面提到過(guò)的HTMLElement類型

HTMLElement類型繼承自Element類型,也是HTML元素的實(shí)際類型针姿,我們?cè)跒g覽器里用的元素都是這個(gè)類型袱吆。

這個(gè)類型都具有一些標(biāo)準(zhǔn)屬性,比如:

id 元素的唯一標(biāo)識(shí)

title 通常是鼠標(biāo)移上去時(shí)候會(huì)顯示的信息

className 類名

等等距淫,這幾個(gè)屬性是可讀寫的绞绒,也就是說(shuō)你改變他們會(huì)得到相應(yīng)的效果。

除了屬性外榕暇,還有幾個(gè)重要的方法

首先說(shuō)說(shuō)操作節(jié)點(diǎn)屬性的方法

getAttribute 蓬衡、setAttribute 、removeAttribute這3個(gè)方法彤枢。

這些是操作屬性最常用的方法了狰晚,怎么用就不說(shuō)了,很簡(jiǎn)單缴啡,顧名思義壁晒。

還有一個(gè)attributes屬性,保存了元素的全部屬性业栅。

這里停下來(lái)秒咐,出個(gè)問(wèn)題,ele.className 和 ele.getAttribute(“class”)返回的結(jié)果是不是同一個(gè)東西碘裕?

解答這個(gè)問(wèn)題携取,我要說(shuō)一個(gè)重要知識(shí)點(diǎn),一個(gè)元素的屬性結(jié)構(gòu)是這么來(lái)的帮孔,比如一個(gè)inpnt元素

那么這個(gè)元素的屬性被包含在 input.attributes里面雷滋,比如你在html元素上看到的class、id或者你自己定義的data-test這種屬性你弦。

然后?getAttribute 惊豺、setAttribute 、removeAttribute這3個(gè)方法可以認(rèn)為是快捷的取attributes集合的方法禽作。而直接input.id或者input.className都是直接掛在input下的屬性尸昧,和attributes是同級(jí)的。所以返回的東西也許看過(guò)去一樣旷偿,實(shí)際是不一樣的烹俗,不信你可以試試input.checked這input.getAttribute(“checked”)試試爆侣。

關(guān)于這個(gè)知識(shí)點(diǎn),詳細(xì)的說(shuō)可以再寫一篇文章幢妄,在我的博客從is(“:checked”)說(shuō)起中有談到過(guò)兔仰,大家可以看看這篇文章和文章后的討論,便可以知道是怎么一回事蕉鸳。

總得來(lái)說(shuō)乎赴,這3個(gè)方法通常用了處理自定義的屬性,而不是id潮尝、class等這種“公認(rèn)特性”榕吼。

接下來(lái)說(shuō)說(shuō)創(chuàng)建元素

document.createElement()可以創(chuàng)建一個(gè)元素,比如:

document.createElement("div");

一般之后可以為元素設(shè)置屬性勉失,兩種方法羹蚣,一種是直接node.property還可以node.setAttribute(“propertyName”,”value”)。等

但是做完這些之后乱凿,這個(gè)元素還是沒(méi)有在頁(yè)面中顽素,所以你還得通過(guò)最上面講的類似appendChild這些方法把元素添加到頁(yè)面里面。

在IE中徒蟆,還可以直接穿整個(gè)HTML字符串進(jìn)去胁出,來(lái)創(chuàng)建元素,比如

document.createElement("

test

");

最后后专,元素節(jié)點(diǎn)也支持HTMLDocument類型的那些查找方法划鸽,比如getElementsByTagName。不過(guò)它只會(huì)找自己后代的節(jié)點(diǎn)戚哎。所以可以這么寫代碼

document.getElementById("test").getElementsByTagName("div");? ? // 找到id為test元素下的所有div節(jié)點(diǎn)

五、Text類型

這個(gè)類型很特殊嫂用,也是第三常見(jiàn)類型(第一第二分別就是Document和Element)型凳。

這個(gè)節(jié)點(diǎn)簡(jiǎn)單來(lái)說(shuō)就是一段字符串。

有個(gè)很重要的特征就是嘱函,它沒(méi)有子元素(不過(guò)這個(gè)仔細(xì)想想也知道= =)

訪問(wèn)text節(jié)點(diǎn)的文本內(nèi)容甘畅,可以通過(guò)nodeValue或者data屬性。

下面簡(jiǎn)單說(shuō)說(shuō)它提供的一些方法

appendData();? ? // 在text末尾加內(nèi)容

deleteData(offset, count);? ? // 從offset指定的位置開(kāi)始刪除count個(gè)字符

還有insertDate往弓、replaceData疏唾、splitText等方法,就不一一說(shuō)了函似,用的機(jī)會(huì)很少槐脏,可以用的時(shí)候再查閱。

然后它還有一個(gè)lenght屬性撇寞,返回字符長(zhǎng)度的顿天。

這里說(shuō)一個(gè)常見(jiàn)的坑堂氯。比如下面這個(gè)html結(jié)構(gòu)



    這里,ul的第一個(gè)子節(jié)點(diǎn)(firstChild)是什么呢牌废?第一眼看過(guò)去咽白,肯定認(rèn)為是li了,但是實(shí)際上鸟缕,你會(huì)發(fā)現(xiàn)不是li晶框,而是一個(gè)文本節(jié)點(diǎn)!

    這是因?yàn)闉g覽器認(rèn)為ul和第一個(gè)li之間有空白字符懂从,所以就有文本節(jié)點(diǎn)了三妈。

    這里一個(gè)常見(jiàn)的問(wèn)題就是遍歷ul的childNodes的時(shí)候,遍歷的元素一定要判斷下nodeType是不是等于1(等于1就代表是元素節(jié)點(diǎn))莫绣,這樣才能跳過(guò)這個(gè)坑畴蒲。否則你也可以刪除所有的空格和換行符。

    創(chuàng)建文本節(jié)點(diǎn)的方法是document.createTextNode

    然后接下來(lái)和操作Element類型一樣对室,就是再插入到元素中模燥,瀏覽器就可以看到了。

    六掩宜、其他的一些類型 Comment蔫骂、DocumentType和DocumentFragment

    這些不常用的一句話帶過(guò)把

    Comment是注釋節(jié)點(diǎn)

    DocumentType就是doctype節(jié)點(diǎn),通過(guò)docment.doctype來(lái)訪問(wèn)

    DocumentFragment這個(gè)節(jié)點(diǎn)是一個(gè)文檔片段牺汤,偶爾會(huì)用到辽旋。

    比如一種常見(jiàn)的用法是,在一個(gè)ul中插入3個(gè)li檐迟。

    如果你循環(huán)插入3次补胚,那么瀏覽器就要渲染3次,對(duì)性能有蠻大的影響追迟。

    所以大家一般這么做

    var fragment = document.createDocumentFragment();

    然后循環(huán)把li溶其,用appendChild插入到fragment里面

    最后在一次把fragment插入到ul里面。這樣就會(huì)很快敦间。

    七瓶逃、DOM擴(kuò)展

    進(jìn)過(guò)上面講的這么多節(jié)點(diǎn)類型,想必大家對(duì)DOM節(jié)點(diǎn)已經(jīng)有了很深的了解廓块,下面講一講DOM擴(kuò)展的一些東西厢绝。

    瀏覽器為了方便開(kāi)發(fā)者,擴(kuò)展了一些DOM功能带猴。

    因?yàn)槭菫g覽器自己擴(kuò)展的昔汉,所以使用前兼容性問(wèn)題一定要注意

    判斷“標(biāo)準(zhǔn)模式”和“混雜模式”通過(guò) document.compatMode和新的document.documentMode

    上面不是說(shuō)了一個(gè)文本節(jié)點(diǎn)作為第一子元素的坑嗎,所以瀏覽器又實(shí)現(xiàn)了一個(gè)children屬性浓利,這個(gè)屬性只包含元素節(jié)點(diǎn)挤庇。

    為了方便判斷A節(jié)點(diǎn)是不是B節(jié)點(diǎn)的子節(jié)點(diǎn)钞速,引入了contains方法,比如

    B.contains(A);? ? // true就代表是嫡秕,false就代表不是

    這個(gè)方法有兼容性問(wèn)題渴语,使用前可以谷歌解決方法。

    針對(duì)訪問(wèn)元素昆咽,又提供了4個(gè)方法innerText/innerHTML/outerTEXT/outerHTML驾凶。

    通過(guò)這些方法,可以讀和寫元素掷酗。

    其中调违,*TEXT是返回文本內(nèi)容?*HTML是返回html文本。

    而outer*則是代表是否包含元素本身泻轰。

    實(shí)際使用來(lái)看技肩,在讀內(nèi)容的時(shí)候 inner*和outer*沒(méi)有區(qū)別。

    在把內(nèi)容寫入元素的時(shí)候浮声,就是是否包含元素本身的區(qū)別虚婿。

    重要的是,這幾個(gè)方法有性能問(wèn)題泳挥,比如在IE中然痊,通過(guò)inner*刪除的節(jié)點(diǎn),其綁定的事件依然在內(nèi)存中屉符,就很容易消耗大量?jī)?nèi)存剧浸。

    還有一個(gè)技巧是,插入大量的html代碼矗钟,用innerHTML是非乘粝悖快的,建議使用真仲。

    八袋马、總結(jié)

    首先感謝所有看到這里的朋友,哈哈秸应,關(guān)于DOM的東西實(shí)在是太多了,不過(guò)這也算是最重要的一個(gè)前端知識(shí)點(diǎn)之一吧碑宴。文章比較長(zhǎng)软啼,也許有點(diǎn)乏味,不過(guò)希望你們耐著性子看完后可以有所收貨^ ^延柠。

    最后編輯于
    ?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
    • 序言:七十年代末祸挪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子贞间,更是在濱河造成了極大的恐慌贿条,老刑警劉巖雹仿,帶你破解...
      沈念sama閱讀 211,042評(píng)論 6 490
    • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異整以,居然都是意外死亡胧辽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
      沈念sama閱讀 89,996評(píng)論 2 384
    • 文/潘曉璐 我一進(jìn)店門公黑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)邑商,“玉大人,你說(shuō)我怎么就攤上這事凡蚜∪硕希” “怎么了?”我有些...
      開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
    • 文/不壞的土叔 我叫張陵朝蜘,是天一觀的道長(zhǎng)恶迈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)谱醇,這世上最難降的妖魔是什么暇仲? 我笑而不...
      開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
    • 正文 為了忘掉前任,我火速辦了婚禮枣抱,結(jié)果婚禮上熔吗,老公的妹妹穿的比我還像新娘。我一直安慰自己佳晶,他們只是感情好桅狠,可當(dāng)我...
      茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
    • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著轿秧,像睡著了一般中跌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菇篡,一...
      開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
    • 那天漩符,我揣著相機(jī)與錄音,去河邊找鬼驱还。 笑死嗜暴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的议蟆。 我是一名探鬼主播闷沥,決...
      沈念sama閱讀 38,902評(píng)論 3 405
    • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼咐容!你這毒婦竟也來(lái)了舆逃?” 一聲冷哼從身側(cè)響起,我...
      開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
    • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎路狮,沒(méi)想到半個(gè)月后虫啥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
      沈念sama閱讀 44,110評(píng)論 1 303
    • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奄妨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
      茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
    • 正文 我和宋清朗相戀三年涂籽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片展蒂。...
      茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
    • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡又活,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锰悼,到底是詐尸還是另有隱情柳骄,我是刑警寧澤,帶...
      沈念sama閱讀 34,258評(píng)論 4 328
    • 正文 年R本政府宣布箕般,位于F島的核電站耐薯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏丝里。R本人自食惡果不足惜曲初,卻給世界環(huán)境...
      茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
    • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杯聚。 院中可真熱鬧臼婆,春花似錦、人聲如沸幌绍。這莊子的主人今日做“春日...
      開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
    • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)傀广。三九已至颁独,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伪冰,已是汗流浹背誓酒。 一陣腳步聲響...
      開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
    • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贮聂,地道東北人靠柑。 一個(gè)月前我還...
      沈念sama閱讀 46,271評(píng)論 2 360
    • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吓懈,于是被迫代替她去往敵國(guó)和親病往。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
      茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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