一篇文章帶你吃透JavaScript中的DOM知識及用法
一、前言
DOM:Document Object Model(文檔對象模型),定義了用戶操作文檔對象的接口,可以說DOM是自HTML將網(wǎng)上相關(guān)文檔連接起來后最偉大的創(chuàng)新。它使得用戶對HTML有了空前的訪問能力,并使開發(fā)者將HTML作為XML文檔來處理。
本文知識導圖如下:
二、DOM框架
DOM是網(wǎng)頁的核心結(jié)構(gòu),無論是HTML、CSS還是JavaScript,都和DOM 密切相關(guān)。HTML的作用是構(gòu)建DOM結(jié)構(gòu),CSS的作用是設(shè)定樣式,JavaScript的作用是讀取以及控制、修改DOM。
當網(wǎng)頁被加載時,瀏覽器會創(chuàng)建頁面的文檔對象模型(Document Object Model)。
如下面一段HTML代碼可以用 HTML DOM 樹來表示:
代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>window</title> </head> <body> <h2><a href="#" rel="external nofollow" rel="external nofollow" >標題文本</a></h2> <p>段落文本</p> <ul class="ul_Web"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> </body> </html>
HTML DOM 樹
在這個樹狀圖中,html位于位于最頂端,它沒有父輩,也沒有兄弟,被稱為DOM的根節(jié)點。更深入一層會發(fā)現(xiàn),html有head和body兩個分支,它們在同一層而不相互包含,它們之間是兄弟關(guān)系,有著共同的父元素html。再往深會發(fā)現(xiàn)head有兩個子元素meta和title,它們互為兄弟。而body有三個子元素,分別是h2、p和ul。再繼續(xù)深入還會發(fā)現(xiàn)h2和ul都有自己的子元素。
通過這樣的關(guān)系劃分,整個HTML文件的結(jié)構(gòu)清晰可見,各元素之間的關(guān)系很容易表達出來,這正是DOM所實現(xiàn)的。
通過可編程的對象模型,JavaScript 獲得了足夠的能力來創(chuàng)建動態(tài)的 HTML。
- JavaScript 能夠改變頁面中的所有 HTML 元素
- JavaScript 能夠改變頁面中的所有 HTML 屬性
- JavaScript 能夠改變頁面中的所有 CSS 樣式
- JavaScript 能夠?qū)撁嬷械乃惺录龀龇磻?/li>
三、認識DOM節(jié)點
節(jié)點(node)最初源于計算機網(wǎng)絡(luò)中,代表著網(wǎng)絡(luò)中的一個連接點,可以說網(wǎng)絡(luò)就是由節(jié)點構(gòu)成的集合。DOM節(jié)點也是類似,文檔是由節(jié)點構(gòu)成的集合。
在DOM中有3種節(jié)點,分別是元素節(jié)點、文本節(jié)點和屬性節(jié)點。
元素節(jié)點:可以說整個DOM都是由元素節(jié)點構(gòu)成的,如:html、body、meta、h2、p、li等都是元素節(jié)點。元素節(jié)點可以包含其他的元素(除了html根元素)。
文本節(jié)點:在HTML中光有元素節(jié)點搭建的框架是不夠的,頁面開發(fā)的最終目的是向用戶展示內(nèi)容。例如li元素中含有的文本信息,每個li之間的換行操作都是文本節(jié)點。并不是所有的元素節(jié)點中都含有文本節(jié)點。
如下例:p元素中直接包含元素節(jié)點,無文本節(jié)點。
<p><span></span></p>
屬性節(jié)點:作為頁面中的元素,或多或少會有一些屬性,如下面例子中,含有了,class、title、color等屬性。開發(fā)者可以利用這些屬性來對包含在元素里的對象作出更準確的描述。
例子:
<a class="a_css" title="CSS" color="#0F0" href="#" rel="external nofollow" rel="external nofollow" >進入CSS網(wǎng)站</a>
由于屬性總是被放在元素里,因此屬性節(jié)點總是被包含在元素節(jié)點中。
上例中可以看出各種節(jié)點的關(guān)系如下圖:
四、JS訪問DOM
每個DOM節(jié)點都有一系列的屬性、方法可以使用。下表中總結(jié)了常用的節(jié)點屬性和方法,供使用時快速查閱。
屬性/方法 | 類型/返回類型 | 說明 |
---|---|---|
nodeName | String | 節(jié)點名稱,根據(jù)節(jié)點的類型而定義 |
nodeValue | String | 節(jié)點的值,同樣根據(jù)節(jié)點的類型而定義 |
nodeType | Number | 節(jié)點類型 |
firstChild | Node | 指向childNodes列表中的第一個節(jié)點 |
lastChild | Node | 指向childNodes列表中的最后一個節(jié)點 |
childNodes | NodeList | 所有子節(jié)點的列表,方法item(i)可以訪問第i+1個節(jié)點 |
parentNode | Node | 指向節(jié)點的父節(jié)點,如果該節(jié)點已是根節(jié)點,則返回null |
previousSibling | Node | 指向節(jié)點的前一個兄弟節(jié)點,如果該節(jié)點已是第一個節(jié)點,則返回null |
nextSibling | Node | 指向節(jié)點的后一個兄弟節(jié)點,如果該節(jié)點已是最后一個節(jié)點,則返回null |
hasChildNodes | Boolean | 當childNodes包含一個或多個節(jié)點時,返回true |
attributes | NameNodeMap | 包含一個元素特征的Attr對象,僅用于元素節(jié)點 |
appendChild | Node | 將node節(jié)點添加到childNodes的末尾 |
removeChild | Node | 從childNodes中刪除node節(jié)點 |
replaceChild | Node | 將childNodes中的oldnode節(jié)點替換成newnode節(jié)點 |
insetBefore | Node | 在childNodes中的refnode節(jié)點之前插入newnode節(jié)點 |
1、獲取節(jié)點
- document.getElementById【通過id屬性獲取對象】
因為id具有唯一性,所以通過該方法獲取的html元素只有一個。 - document.getElementsByTagName【通過標簽名獲取對象】
獲取的元素有一個或者多個,將獲取的元素存儲在一個類似數(shù)組的集合中,獲取的元素通過下標來區(qū)分(下標從0開始) - document.getElementsByClassName【通過class屬性獲取對象】
不同的標簽可以使用同一個class屬性值,所以獲取的元素由一個或者多個,獲取的元素存儲在一個類似數(shù)組的集合中,獲取的元素通過下標來區(qū)分(下標從0開始) - querySelector【根據(jù)id、標簽、class名獲取元素】
不管是哪一個名稱,都獲取第一個元素 - querySelectorAll【獲取所有執(zhí)行標簽名/帶有指定class名的元素】
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>dom操作</title> </head> <body> <h2>登鸛雀樓</h2> <p id="test">白日依山盡,</p> <p>黃河入海流。</p> <p class="demo">欲窮千里目,</p> <p>更上一層樓。</p> <div class="demo">div標簽1</div> <div>div標簽2</div> </body> <script> //獲取html元素 //getElementById('id屬性值'):通過標簽的id屬性來獲取元素,因為id具有唯一性,所以通過該方法獲取的html元素只有一個 var ele =document.getElementById('test'); console.log(ele); // ele.style.color = '#f00'; console.log('--------------------'); //getElementsByTagName('標簽名'):通過標簽名稱獲取元素,獲取的元素由一個或者多個,將獲取的元素存儲在一個類似數(shù)組的集合中,獲取的元素通過下標來區(qū)分(下標從0開始) var pEles=document.getElementsByTagName('p'); console.log(pEles); console.log(pEles[2]); console.log('獲取的p元素個數(shù):'+pEles.length); console.log('--------------------'); //getElementsByClassName('class屬性值'):通過標簽的class屬性值來獲取元素,不同的標簽可以使用同一個class屬性值,所以獲取的元素由一個或者多個,獲取的元素存儲在一個類似數(shù)組的集合中 var elements =document.getElementsByClassName('demo'); console.log(elements); //querySelector('id名/標簽名/class名'):根據(jù)id、標簽、class名獲取元素,不管是哪一個名稱,都獲取第一個元素 var result1 =document.querySelector('#test'); console.log(result1); var result2 = document.querySelector('.demo'); console.log(result2); var result3 =document.querySelector('p'); console.log(result3); console.log('--------------------'); //querySelectorAll('標簽名/class名'):獲取所有執(zhí)行標簽名/帶有指定class名的元素 var demoEles=document.querySelectorAll('.demo'); console.log(demoEles); var pElemets = document.querySelectorAll('p'); console.log(pElemets); </script> </html>
2、改變 HTML
語法 | 說明 |
---|---|
document.write() | 改變 HTML 輸出流 |
對象.innerHTML=新的 HTML | 改變 HTML 內(nèi)容(包含元素、屬性、文本) |
對象.innerText=文本內(nèi)容 | 改變HTML文本內(nèi)容 |
對象.attribute=新屬性值 | 改變 HTML 屬性 |
例1:document.write()應用
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>write()</title> <style> .ddd{ color: #00f; background-color: #ccc; } </style> </head> <body> 大湖名城,創(chuàng)新高地2 <button onclick="show()">點我一下</button> </body> <script> document.write('<br />大湖名城,創(chuàng)新高地<br />'); document.write('<h2>二級標題標簽</h2>') document.write('<p style="color:#f00;">段落標簽</p>') document.write('<p class="ddd">段落標簽2</p>'); function show(){ document.write('hello js!'); } </script> </html>
運行效果:
例2:innerHTML和innerText應用
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>innertHTML和innerText</title> </head> <body> <ul> <li>列表項第一項</li> <li>列表項第二項</li> </ul> <div></div> </body> <script> //獲取ul元素 var ulEle =document.querySelector('ul'); ulEle.innerHTML = '<li>大湖名城,創(chuàng)新高地</li>'; var result1 =ulEle.innerHTML; console.log(result1); var result2 =ulEle.innerText; console.log(result2); //獲取div標簽 var divEle = document.querySelector('div'); divEle.innerText = '<h2>大湖名城,創(chuàng)新高地</h2>'; </script> </html>
運行效果:
例3:通過改變img固有屬性值實現(xiàn)兩張圖片切換
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>改變和獲取標簽固有屬性值</title> </head> <body> <img src="img/lyf.jpg" width="300px"/> <button onclick="changeImg()">換一張圖片</button> </body> <script> //獲取img標簽 var imgEle = document.querySelector('img'); var flag = true; function changeImg(){ if(flag){ imgEle.src='img/wzx.jpg'; imgEle.style.width='500px'; flag=false; }else{ imgEle.src='img/lyf.jpg'; imgEle.style.width='300px'; flag=true; } } </script> </html>
運行效果:
3、改變 CSS
通過JavaScript改變CSS樣式的思路:先獲取需要改變樣式的元素,可通過document.querySelector()方法獲取。然后再通過上圖語法中的方法去改變元素的樣式。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>改變元素的樣式</title> </head> <body> <p class="demo">大湖名城,創(chuàng)新高地</p> <button onclick="addCss()">給P標簽添加樣式</button> </body> <script> //獲取p標簽 var pEle = document.querySelector('p'); function addCss(){ pEle.style.color= '#f00'; pEle.style.backgroundColor = '#ccc'; pEle.style.fontSize = '30px'; pEle.style.textDecoration='underline'; } </script> </html>
運行效果:
4、檢測節(jié)點類型
通過節(jié)點的nodeType屬性可以檢測出節(jié)點的類型。該屬性返回一個代表節(jié)點類型的整數(shù)值,總共有12個可取的值,例如:
console.log(document.nodeType);
上面代碼執(zhí)行后的返回值為9,表示DOCUMENT_NODE類型節(jié)點。然而實際上,對于大多數(shù)情況而言,真這行游泳的還是前面我們學過的3種節(jié)點,即元素節(jié)點、文本節(jié)點和屬性節(jié)點,他們的nodeType值如下:
元素節(jié)點
nodeType值為1
屬性節(jié)點
nodeType值為2
文本節(jié)點
nodeType值為3
這就意味著可以對某種類型的節(jié)點做單獨的處理,在搜索節(jié)點的時候非常實用。
5、操作節(jié)點間的父子及兄弟關(guān)系
【1】通過父節(jié)點找到子節(jié)點
父子兄弟關(guān)系是DOM模型中節(jié)點之間非常重要的關(guān)系,在獲取某個節(jié)點之后,可以通過父子關(guān)系,利用hasChildNodes()方法和childNodes屬性獲取該節(jié)點所包含的所有子節(jié)點。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <ul> <li>Java</li> <li>Node.js</li> <li>JavaScript</li> </ul> </body> <script> //獲取ul標記 var ulEle=document.querySelector("ul"); var DOMString=""; if(ulEle.hasChildNodes()){ for (var s of ulEle.childNodes) { DOMString+=s.nodeName+"\n"; } } console.log(DOMString); </script> </html>
這個案例的首先獲取ul標記,然后利用hasChildNodes()判斷其是否有子節(jié)點,如果有則利用childNodes遍歷它的所有子節(jié)點。執(zhí)行后控制臺輸出結(jié)果可以看到ul包括4個文本節(jié)點和3個元素節(jié)點。
運行效果:
除了獲取全部子節(jié)點,DOM還提供了firstChild和lastChild兩個屬性,分別表示獲取父元素的第一個子元素和最后一個子元素。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <ul> <li>Java</li> <li>Node.js</li> <li>JavaScript</li> </ul> </body> <script> //獲取ul標記 var ulEle = document.querySelector('ul'); //獲取ul的第一個子節(jié)點 var firstEle = ulEle.firstChild; console.log(firstEle); //獲取ul的最后一個子節(jié)點 var lastEle = ulEle.lastChild; console.log(lastEle); </script> </html>
運行效果:
【2】通過子節(jié)點找到父節(jié)點
利用parentNode屬性,可以找到一個節(jié)點的父節(jié)點。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <ul> <li>列表項第1項</li> <li>列表項第2項</li> <li class="demo">列表項第3項</li> <li>列表項第4項</li> <li>列表項第5項</li> </ul> </body> <script> //獲取class名為demo的li元素 var demoClassLiEle = document.querySelector('.demo'); console.log(demoClassLiEle); //獲取li元素的父節(jié)點ul元素 var liParentEle = demoClassLiEle.parentNode; console.log(liParentEle); liParentEle.style.color = '#f00'; </script> </html>
這個案例首先通過class名為demo來獲取子節(jié)點li標記,再通過parentNode屬性,成功找到了子節(jié)點li指定的父節(jié)點,然后通過父節(jié)點的style樣式來改變ul父節(jié)點的樣式。運行代碼后,原先的整體color樣式,由默認顏色變?yōu)?f00。
運行效果:
var liParentEle = demoClassLiEle.parentNode;
liParentEle.style.color = ‘#f00’;
上面兩句代碼可以用鏈式編程的方式連寫:
demoClassLiEle.parentNode.style.backgroundColor=‘#f00’;
我們還可以一直向上搜索父節(jié)點。比如上面案例,我們可以通過兩次parentNode來獲取body標記。
利用鏈式編程修改上述案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <ul> <li>列表項第1項</li> <li>列表項第2項</li> <li class="demo">列表項第3項</li> <li>列表項第4項</li> <li>列表項第5項</li> </ul> </body> <script> //獲取class名為demo的li元素 var demoClassLiEle = document.querySelector('.demo'); console.log(demoClassLiEle); //利用鏈式編程獲取body元素 demoClassLiEle.parentNode.parentNode.style.backgroundColor='#f00'; </script> </html>
運行效果:整個body元素的背景顏色都被修改了。
【3】通過某個節(jié)點找到它的兄弟節(jié)點
在DOM中父子關(guān)系屬于兩個不同層次之間的關(guān)系,而在同一層中常用的便是兄弟關(guān)系。
DOM中提供了nextSibling和previousSibling屬性來訪問兄弟節(jié)點。nextSibling是訪問選中節(jié)點的下一個節(jié)點,且可以一直向下搜索兄弟節(jié)點;previousSibling是訪問選中節(jié)點的上一個節(jié)點,且可以一直向上搜索兄弟節(jié)點。
案例代碼:通過class為Demo的li標記來獲取他的前一個li和后一個li(思路:li之間存在文本節(jié)點,所以在這里我們可以利用鏈式編程兩次運用屬性找尋。)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <ul> <li>Java</li> <li class="demo">Node.js</li> <li>JavaScript</li> </ul> </body> <script> //獲取class名為demo的li元素 var demoClassLiEle = document.querySelector('.demo'); //獲取li元素的上一個節(jié)點元素(兄弟元素) var previousSiblingEle = demoClassLiEle.previousSibling; console.log(previousSiblingEle); //獲取li元素的下一個節(jié)點元素(兄弟元素) var nextSiblingEle = demoClassLiEle.nextSibling; console.log(nextSiblingEle); </script> </html>
運行效果:
6、操作節(jié)點屬性
在找到需要的節(jié)點之后通常希望對其屬性做相應的設(shè)置。DOM定義了三個便捷的方法來查詢、設(shè)置和刪除節(jié)點的屬性,即getAttribute()、setAttribute()和removeAttribute()。
這三個方法不能通過document對象來調(diào)用,只能通過一個元素節(jié)點的對象來調(diào)用。下面一個例子將實現(xiàn)三個方法的具體操作,帶您一步學會三種方法的應用,您可以先注釋其它兩種方法來看某種方法的實現(xiàn)效果。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>屬性操作</title> </head> <body> <img src="img/lyf.jpg" width="300px" /> </body> <script> var imgEle = document.querySelector('img'); //獲取img標簽的屬性值 var srcValue =imgEle.getAttribute('src'); console.log(srcValue); console.log(imgEle.src); var widthValue = imgEle.getAttribute('width'); console.log(widthValue); //設(shè)置img標簽的屬性值 //imgEle.setAttribute('src','img/wzx.jpg'); //imgEle.setAttribute('width','800px'); //刪除img標簽的屬性值 //imgEle.removeAttribute('width'); //imgEle.removeAttribute('src'); </script> </html>
7、創(chuàng)建和操作節(jié)點
除了查找節(jié)點并處理節(jié)點的屬性外,DOM同樣提供了很多便捷的方法來管理節(jié)點,主要包括創(chuàng)建、刪除、替換、插入等操作。
【1】創(chuàng)建節(jié)點
創(chuàng)建節(jié)點的過程在DOM中比較規(guī)范,而且對于不同類型的節(jié)點方法還略有區(qū)別。
類型 | 方法 |
---|---|
元素節(jié)點 | createElement() |
文本節(jié)點 | createTextNode() |
文檔片段節(jié)點 | createDocumentFragment() |
假設(shè)有如下HTML文件:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>創(chuàng)建新節(jié)點</title> </head> <body> </body> <html>
希望在body中動態(tài)添加如下代碼:
<p>段落標簽</p>
可以利用剛才提到的兩個方法來完成。首先利用createElement()創(chuàng)建p元素,如下所示:
var oP=document.createElement("p");
然后利用createTextNode()方法創(chuàng)建文本節(jié)點,并利用appendChild()方法將其添加到oP節(jié)點的childNodes列表的最后,如下圖所示:
var oText=document.createTextNode("段落標簽"); oP.appendChild(oText);
最后再將已經(jīng)包含了文本節(jié)點的元素(oP節(jié)點)添加到body中,仍采用appendChild()方法,如下所示:
document.body.appendChild(oP);
這樣便完成了body中p元素的創(chuàng)建。appendChild()方法添加對象的位置永遠是在節(jié)點列表的尾部添加。
【2】插入節(jié)點
上面我們介紹了將新創(chuàng)建的元素插入到列表的尾部,如果我們希望這個節(jié)點插入已知節(jié)點之前,則可以采用insertBefore()方法。該方法有兩個參數(shù),第一個參數(shù)是新節(jié)點,第二個參數(shù)是目標節(jié)點。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>節(jié)點的增刪改查</title> </head> <body> <ul> <li>列表項第1項</li> <li class="demo">列表項第2項</li> </ul> </body> <script> //創(chuàng)建一個li元素節(jié)點 var liEle =document.createElement('li'); //創(chuàng)建文本節(jié)點 var textNode =document.createTextNode('列表項第n項'); //將textNode節(jié)點插入到liEle節(jié)點中 liEle.appendChild(textNode); console.log(liEle); //將組裝好的liEle元素添加到ul元素中 var ulELe = document.querySelector('ul') // 將liEle元素放在ul中的第一個位置 ulELe.insertBefore(liEle,ulELe.firstElementChild); </script> </html>
運行效果:
【3】替換節(jié)點
有時候我們需要替換頁面中的某個節(jié)點。DOM提供了replaceChild()方法來完成這項任務(wù),該方法是針對要替換節(jié)點的父節(jié)點來操作的。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>節(jié)點的增刪改查</title> </head> <body> <ul> <li>列表項第1項</li> <li class="demo">列表項第2項</li> </ul> </body> <script> //創(chuàng)建一個li元素節(jié)點 var liEle =document.createElement('li'); //創(chuàng)建文本節(jié)點 var textNode =document.createTextNode('列表項第n項'); //將textNode節(jié)點插入到liEle節(jié)點中 liEle.appendChild(textNode); console.log(liEle); //將組裝好的liEle元素添加到ul元素中 var ulELe = document.querySelector('ul') //將組裝好的liEle元素替換到ul元素中 ulELe.replaceChild(liEle,ulELe.firstElementChild); </script> </html>
運行效果:
【4】刪除節(jié)點
刪除節(jié)點是通過父節(jié)點的removeChild()方法來實現(xiàn)的。通常方法是找到要刪除的節(jié)點,然后利用parentNode屬性找到父節(jié)點,最后利用removeChild()刪除節(jié)點即可。
案例代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>節(jié)點的增刪改查</title> </head> <body> <ul> <li>列表項第1項</li> <li class="demo">列表項第2項</li> </ul> </body> <script> var ulELe = document.querySelector('ul') // 刪除第一個li元素 ulELe.removeChild(ulELe.firstElementChild) </script> </html>
運行效果:
總結(jié)
到此這篇關(guān)于一篇文章帶你吃透JavaScript中DOM知識及用法的文章就介紹到這了,更多相關(guān)JS中DOM知識及用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳細聊聊TypeScript中any unknown never和void的區(qū)別
這篇文章主要給大家聊聊TypeScript 中 any、unknown、never 和 void 有什么區(qū)別,文中有詳細的代碼實例講解,具有一定的參考價值,需要的朋友可以參考下2023-07-07