JavaScript的DOM操作
由于HTML文檔被瀏覽器解析后就是一顆DOM樹佑笋,就需要通過JavaScript來操作DOM备籽。
始終記住DOM是一個(gè)樹形結(jié)構(gòu)离熏。操作DOM節(jié)點(diǎn)實(shí)際上就是有如下幾個(gè)操作:
- 更新:更新該DOM節(jié)點(diǎn)的內(nèi)容,相當(dāng)于更新了該DOM節(jié)點(diǎn)表示HTML的內(nèi)容
- 遍歷:遍歷該DOM節(jié)點(diǎn)下的子節(jié)點(diǎn)殴蹄,以便進(jìn)一步操作
- 添加:在該DOM節(jié)點(diǎn)下新增一個(gè)子節(jié)點(diǎn)哗咆,相當(dāng)于動態(tài)增加了一個(gè)HTML節(jié)點(diǎn)
- 刪除:將該節(jié)點(diǎn)從HTML中刪除蜘欲,相當(dāng)于刪除了該DOM節(jié)點(diǎn)的內(nèi)容以及它包含的所有子節(jié)點(diǎn)
在操作一個(gè)DOM節(jié)點(diǎn)之前,我們需要通過各種方式先拿到這個(gè)DOM節(jié)點(diǎn)晌柬。最常用的方法是getElementById()
和getElementsByTagName()
姥份,以及CSS選擇器getElementsByClassName()
。
由于ID在HTML文檔中是唯一的年碘,所有document澈歉。getElementByID()可以直接定位唯一的一個(gè)DOM節(jié)點(diǎn)
。getElementsByTagName()
和getElementsByClassName()
總是返回一組DOM節(jié)點(diǎn)盛泡。要精確地選擇DOM闷祥,可以先定位父節(jié)點(diǎn),再從父節(jié)點(diǎn)開始選擇,以縮小范圍凯砍。
例如:
//返回id為test的節(jié)點(diǎn)
var test = document.getElementById('test');
//先定位id為table的節(jié)點(diǎn)箱硕,再返回內(nèi)部所有tr節(jié)點(diǎn)
var tests = document.getElementById('table').getElementsByTagName('tr');
//先定位ID為table的節(jié)點(diǎn),再返回其內(nèi)部所有class包含red的節(jié)點(diǎn)
var tests = document.getElementById('table').getElementsByClassName('red');
//獲取節(jié)點(diǎn)test下的所有直屬節(jié)點(diǎn)
var cs = test.child;
//獲取節(jié)點(diǎn)test下第一個(gè)悟衩、最后一個(gè)子節(jié)點(diǎn)
var first = test.firstElementChild;
var last = test.lastElementChild;
第二種方法是使用querySelector()
和querySelectorAll()
剧罩,需要了解selector語法,然后使用條件來獲取節(jié)點(diǎn):
//通過querySelector獲取ID為q1的節(jié)點(diǎn)
var q1 = document.querySelector('#q1');
//通過querySelectorAll獲取q1節(jié)點(diǎn)內(nèi)的符合條件的所有節(jié)點(diǎn)
var ps = q1.querySelectorAll('div.highlighted > p');
低版本的IE < 8 不支持上面兩個(gè)操作座泳,IE8僅有限支持惠昔。
嚴(yán)格地講,我們這里的DOM節(jié)點(diǎn)是指Elment挑势,但是DOM節(jié)點(diǎn)實(shí)際上是Node镇防,在HTML中,Node包括Element潮饱、Comment来氧、CDATA_SECTION等很多種,以及根節(jié)點(diǎn)Document類型香拉,但是啦扬,絕大多數(shù)我們只關(guān)心Element,也就是實(shí)際控制頁面的Node凫碌,其他類型的Node忽略即可扑毡。根節(jié)點(diǎn)Document已經(jīng)自動綁定為全局變量document。
更新DOM
拿到一個(gè)DOM節(jié)點(diǎn)后,我們可以對它進(jìn)行更新≠诵校可以直接修改節(jié)點(diǎn)的文本,方法又兩種:
- 一種是修改innerHTML屬性泉褐,這個(gè)方式非常強(qiáng)大赐写,不但可以修改一個(gè)DOM節(jié)點(diǎn)的文本內(nèi)容鸟蜡,還可以直接通過HTML片段修改DOM節(jié)點(diǎn)內(nèi)部的子樹。
//獲取id為p-id的元素
var p = document.getElementById('p-id');
//設(shè)置文本為ABC
p.innerHTML = 'ABC';
//設(shè)置HTML的屬性
p.innerHTML = 'ABC<span style ="color:red">RED</span>';
用innerHTML時(shí)要注意挺邀,是否需要寫入HTML揉忘。如果寫入的字符是通過網(wǎng)絡(luò)拿到的,要注意對字符進(jìn)行編碼來避免XSS攻擊
- 第二種是修改innerText和textContent屬性端铛,這樣可以自動對字符串進(jìn)行HTML編碼泣矛,保證無法設(shè)置任何HTML標(biāo)簽。
var p = document.getElementById('p-id');
p.innerText = 'abc';
兩者的區(qū)別在于innerText不返回隱藏元素的文本禾蚕,而textContent返回所有文本您朽,注意IE < 9不支持textContent。
此外,修改CSS也是經(jīng)常需要的操作哗总。DOM節(jié)點(diǎn)的style屬性對應(yīng)所有的CSS几颜,可以直接獲取或設(shè)置。因?yàn)镃SS允許font-size這樣的名稱讯屈,但它并非JavaScript有效的屬性名蛋哭,所以需要在JavaScript中改為駝峰式命名fontSize。
var p = document.getElementById('p-id');
//設(shè)置CSS
p.style.color = '#ff0000';
p.style.fontSize = '20px';
p.style.paddingTop = '2em';
插入DOM
當(dāng)我們獲取了某個(gè)DOM節(jié)點(diǎn)之后涮母,如果想要在這個(gè)DOM節(jié)點(diǎn)內(nèi)插入新的DOM節(jié)點(diǎn)谆趾,應(yīng)該如何做?
如這個(gè)DOM是空的叛本,例如沪蓬,<div></div>
,那么,直接使用innerHTML = <span>child</span>
就可以修改DOM節(jié)點(diǎn)的內(nèi)容来候,相當(dāng)于插入例如新的DOM節(jié)點(diǎn)怜跑。
如果這個(gè)DOM節(jié)點(diǎn)不是空的,那就不能這么做吠勘,因?yàn)閕nnerHTML會直接替換掉原來的所有子節(jié)點(diǎn)性芬。這種情況下插入新節(jié)點(diǎn)有兩個(gè)方法:
- 一是使用appendChild,把一個(gè)子節(jié)點(diǎn)添加到父節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)剧防。
<!--源HTML結(jié)構(gòu)-->
<p id="js">JavaScript</p>
<div id="list">
<p id = "java">Java</p>
<p id = "python">python</p>
<p id = "scheme">Scheme</p>
</div>
把<p id="js">JavaScript</p>添加到<div id="list">
的最后一項(xiàng):
var
js = document.getElementById('js');
list = document.getElementById('lis');
list.appendChild(js);
現(xiàn)在植锉,HTML結(jié)構(gòu)變成這樣:
<div id="list">
<p id = "java">Java</p>
<p id = "python">python</p>
<p id = "scheme">Scheme</p>
<p id="js">JavaScript</p>
</div>
因?yàn)槲覀儾迦氲膉s節(jié)點(diǎn)已經(jīng)存在于當(dāng)前的DOM中,因此這個(gè)節(jié)點(diǎn)首先會從原先的位置刪除峭拘,再插入到新的位置俊庇。
下面,我們來看看從零創(chuàng)建一個(gè)新的節(jié)點(diǎn)鸡挠,然后插入到指定的位置:
var
list = document.getElementById('lis');
haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.appendChild(haskell)
這樣我們就動態(tài)添加了一個(gè)新的節(jié)點(diǎn):
<div id="list">
<p id = "java">Java</p>
<p id = "python">python</p>
<p id = "scheme">Scheme</p>
<p id="haskell">Haskell</p>
</div>
動態(tài)創(chuàng)建一個(gè)節(jié)點(diǎn)然后添加到DOM樹中辉饱,可以實(shí)現(xiàn)很多功能。舉個(gè)例子拣展,下面的代碼創(chuàng)建一個(gè)<style>
節(jié)點(diǎn)彭沼,然后把它添加到<head>
節(jié)點(diǎn)的末尾,這樣就動態(tài)地給文檔添加了新的CSS定義:
var d = document.createElement('style');
d.setAttribute('type','text/css');
d.innerHTML = 'p{color:red}';
document.getElementsByTagName('head')[0].appendChild(d);
- 二是使用insertBefore备埃,把子節(jié)點(diǎn)插入到指定的位置姓惑。使用parentElement.insertBefore(newElement,referenceElement),子節(jié)點(diǎn)會插入到referenceElement之前。
還是以上面HTML為例按脚,假設(shè)我們要把Haskell插入到Python之前:
<div id="list">
<p id = "java">Java</p>
<p id = "python">python</p>
<p id = "scheme">Scheme</p>
</div>
可以這么寫:
var
list = document.getElementById('lis');
ref = document.getElementById('python');
haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.insertBefore(haskell,ref);
新的HTML結(jié)構(gòu)如下
<div id="list">
<p id = "java">Java</p>
<p id = "haskell">Haskell</p>
<p id = "python">python</p>
<p id = "scheme">Scheme</p>
</div>
可見于毙,使用insertBefore重點(diǎn)是要拿到一個(gè)參考子節(jié)點(diǎn)的引用。很多時(shí)候辅搬,需要一個(gè)父節(jié)點(diǎn)的所有子節(jié)點(diǎn)唯沮,可以通過迭代children屬性來實(shí)現(xiàn)。
var
i,c,
list = document.getElementById('list');
for(i = 0; i < list.children.length; i++) {
c = list.children[i]; //拿到第i個(gè)子節(jié)點(diǎn)
}
刪除DOM
刪除一個(gè)DOM節(jié)點(diǎn)就比插入要容易得多。要刪除一個(gè)節(jié)點(diǎn)介蛉,首先要獲得該節(jié)點(diǎn)本身以及它的父節(jié)點(diǎn)夯缺,然后,調(diào)用父節(jié)點(diǎn)的removeChild把自己刪掉:
//拿到帶刪除節(jié)點(diǎn)
var self = document.getElementById('removed');
//拿到父節(jié)點(diǎn)
var parent = self.parentElement;
//刪除
var removed = parent.removeChild(self);
removed == self; //true
注意到刪除的DOM節(jié)點(diǎn)雖然不在文檔樹了甘耿,但其實(shí)它還在內(nèi)存中踊兜,可以隨時(shí)再次被添加到別的位置。
當(dāng)你遍歷一個(gè)父節(jié)點(diǎn)的子節(jié)點(diǎn)并進(jìn)行刪除操作時(shí)佳恬,要注意捏境,children屬性只是一個(gè)只讀屬性,并且它在子節(jié)點(diǎn)變化時(shí)會實(shí)時(shí)更新毁葱。
例如垫言,對如下的HTML結(jié)構(gòu):
<div id="parent" >
<p>First</P>
<p>Second</P>
</div>
當(dāng)我們用如下代碼刪除子節(jié)點(diǎn)時(shí):
var parent = document.getElementById('parent');
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); //瀏覽器報(bào)錯
瀏覽器報(bào)錯:parent.children[1]不是一個(gè)有效節(jié)點(diǎn)。原因就在于倾剿,當(dāng)<p>First</P>
節(jié)點(diǎn)被刪除后筷频,parent.children的節(jié)點(diǎn)數(shù)量已經(jīng)從2變?yōu)?,索引[1]已經(jīng)不存在了前痘。因此凛捏,當(dāng)刪除多個(gè)節(jié)點(diǎn)的時(shí),要注意children屬性時(shí)刻都在變化芹缔。