由于 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]
已经不存在了。