由于 HTML 文檔被瀏覽器解析后就是一棵 DOM 樹,要改變 HTML 的結構,就需要通過 JavaScript 來操作 DOM。
操作一個 DOM 節點實際上就是這么幾個操作:
- 更新:更新該 DOM 節點的內容,相當于更新了該 DOM 節點表示的HTML 的內容;
- 遍歷:遍歷該 DOM 節點下的子節點,以便進行進一步操作;
- 添加:在該 DOM 節點下新增一個子節點,相當于動態增加了一個 HTML 節點;
- 刪除:將該節點從 HTML 中刪除,相當于刪掉了該 DOM 節點的內容以及它包含的所有子節點。
獲取節點元素
在操作一個 DOM 節點前,我們需要通過各種方式先拿到這個 DOM 節點。
最常用的方法是:
- document.getElementById()
- document.getElementsByTagName()
- 以及 CSS 選擇器 document.getElementsByClassName()
由于ID
在 HTML 文檔中是唯一的,所以document.getElementById()
可以直接定位唯一的一個DOM
節點;
document.getElementsByTagName()
和document.getElementsByClassName()
總是返回一組 DOM 節點。要精確地選擇 DOM,可以先定位父節點,再從父節點開始選擇,以縮小范圍。
// 返回ID為'test'的節點:
var test = document.getElementById('test');
// 先定位ID為'test-table'的節點,再返回其內部所有tr節點:
var trs = document.getElementById('test-table').getElementsByTagName('tr');
// 先定位ID為'test-div'的節點,再返回其內部所有class包含red的節點:
var reds = document.getElementById('test-div').getElementsByClassName('red');
// 獲取節點test下的所有直屬子節點:
var cs = test.children;
// 獲取節點test下第一個、最后一個子節點:
var first = test.firstElementChild;
var last = test.lastElementChild;
第二種方法是使用querySelector()
和querySelectorAll()
,需要了解selector
語法,然后使用條件來獲取節點,更加方便:
// 通過 querySelector 獲取 ID 為 q1 的節點:
var q1 = document.querySelector('#q1');
// 通過 querySelectorAll 獲取 q1 節點內的符合條件的所有節點:
var ps = q1.querySelectorAll('div.highlighted > p');
更新 DOM
拿到一個 DOM 節點后,我們可以對它進行更新,直接修改節點的文本
方式一:修改innerHTML屬性,這個方式非常強大,不但可以修改一個DOM節點的文本內容,還可以直接通過HTML片段修改DOM節點內部的子樹
// 獲取<p id="p_id">...</p>
var p = document.getElementById('p_id');
// 設置文本為ABC:
p.innerHTML = 'ABC'; // <p id="p-id">ABC</p>
// 設置HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// <p>...</p>的內部結構已修改
方式二: 修改innerText
或textContent
屬性,這樣可以自動對字符串進行 HTML 編碼,保證無法設置任何HTML標簽
innerText
或textContent
的區別在于讀取屬性時,innerText
不返回隱藏元素的文本,而textContent
返回所有文本
// 獲取<p id="p_id">...</p>
var p = document.getElementById('p_id');
// 設置文本:
p.innerText = '<script>alert("Hi")</script>';
// HTML被自動編碼,無法設置一個<script>節點:
// <p id="p-id"><script>alert("Hi")</script></p>
插入 DOM
獲得某個 DOM 節點,然后在這個 DOM 節點內插入新的 DOM
方式一:使用appendChild,把一個子節點添加到父節點的最后一個子節點
例如:
<!-- HTML結構 -->
<p id="js">JavaScript</p>
<div id="list">
<p id="java">Java</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
</div>
將 id
為 js
的標簽插入到 <div>...</div> 便簽最后一項
var js = document.getElementById('js');
var list = document.getElementById('list');
list.appendChild(js);
結果:
<!-- HTML結構 -->
<div id="list">
<p id="java">Java</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
<p id="js">JavaScript</p>
</div>
上面是把已知的元素插入到指定位置,但更多的時候我們會從零創建一個新的節點,然后插入到指定位置:
var list = document.getElementById('list');
var swift = document.createElement('p');
swift.id = 'swift';
swift.innerText = 'Swift';
list.appendChild(swift);
結果:
<!-- HTML結構 -->
<div id="list">
<p id="java">Java</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
<p id="swift">Swift</p>
</div>
方式二: 使用 insertBefore 把子節點插入到指定的位置
使用parentElement.insertBefore(newElement, referenceElement);
,子節點會插入到referenceElement
之前。
還是以上面的 HTML 為例,假定我們要把Swift
插入到Python
之前:
<!-- HTML結構 -->
<div id="list">
<p id="java">Java</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
</div>
var list = document.getElementById('list');
var ref = document.getElementById('python');
var swift = document.createElement('p');
swift.id = 'swift';
swift.innerText = 'Swift';
list.insertBefore(swift, ref);
<!-- HTML結構 -->
<div id="list">
<p id="java">Java</p>
<p id="swift">Swift</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
</div>
刪除DOM
刪除一個節點,首先要獲得該節點本身以及它的父節點,然后,調用父節點的removeChild
把自己刪掉
// 拿到待刪除節點:
var self = document.getElementById('to-be-removed');
// 拿到父節點:
var parent = self.parentElement;
// 刪除:
var removed = parent.removeChild(self);
removed === self; // true
注意,當從子節點數組中刪除節點時,需要防止數組越界
<div id="parent">
<p>First</p>
<p>Second</p>
</div>
執行刪除操作
var parent = document.getElementById('parent');
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); // <-- 瀏覽器報錯
報錯原因:
當<p>First</p>
節點被刪除后,parent.children
的節點數量已經從2
變為了1
,索引[1]
已經不存在了。