浏览器解析完 HTML 后,创建的 DOM 元素会组成树状结构,浏览器上呈现的画面,就是根据 DOM 树绘制出来,只要改变 DOM 树,浏览器就会根据改变后的 DOM 树重绘画面,而这就构成动态修改文件的基本原理。
底下这个范例示范如何动态新增与删除图片:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<input id="src" type="text"><button id="add">新增图片</button>
<div id="images"></div>
<script type="text/javascript">
document.getElementById('add').onclick = function() {
let img = document.createElement('img');
img.src = document.getElementById('src').value;
img.onclick = function() {
document.getElementById('images').removeChild(this);
};
document.getElementById('images').appendChild(img);
};
</script>
</body>
</html>
按我看执行结果。
在原本的 HTML 中,并没有任何的<img>
元素,当在文本框中输入图片的网址并按下按钮时,会使用document
的createElement
来动态创建元素,此时这个元素并没有绑定至 DOM 树,所以还不会出现在画面上。
接着你设定创建的图片元素src
为输入的网址,并注册按下图片时,使用removeChild
将图片本身(this
)从id
为images
的<div>
中移除。
最后,将这个动态创建的图片元素使用appendChild
附加至id
为images
的<div>
元素成为其子元素,此时浏览器根据 DOM 树结构重绘画面。
当使用 JavaScript 动态改变 DOM 树时,在浏览器的检视网页源码中,是看不到动态调整后的 HTML(那是一开始加载的静态 HTML),你要使用浏览器中的开发者工具,才能看到动态的 DOM 画面。例如 Chrome 的「开发人员工具」:

每个节点都只能有一个父节点,如果直接获取 DOM 树中既有的节点,并使用appendChild
将之附加至另一个节点,则表示节点会从原有的父节点脱离,再附加至另一节点。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
容器一:
<div id="container1">
<img id="image" src="https://openhome.cc/Gossip/images/caterpillar_small.jpg"/>
</div><br>
容器二:
<div id="container2"></div>
<script type="text/javascript">
document.getElementById('image').onclick = function() {
let container1 = document.getElementById('container1');
let container2 = document.getElementById('container2');
if(this.parentNode === container1) {
container2.appendChild(this);
}
else {
container1.appendChild(this);
}
};
</script>
</body>
</html>
按我看执行结果。
在这个例子中,点选图片,会将图片来回附加于两个<div>
之间,由于一个节点只能有一个父节点,所以appendChild
的动作,会使被附加的节点从原父节点脱离。
createElement
是用来创建标签对应的元素,如果要创建文本节点,必须使用createTextNode
,如果要动态创建属性,则使用createAttribute
(少用)。
例如,若有个<div id="console"></div>
,想要在其中附加文本,可以如下:
let text = document.createTextNode('your text ....');
document.getElementById('console').appendChild(text);
也可以使用insertBefore
、replaceChild
等方法来调整 DOM 树上的节点,各种方法的说明可以参考〈JavaScript and HTML DOM Reference〉。
要注意的是,只要你将节点附加至 DOM,浏览器就会重绘画面,若有大量的节点要创建,每次创建就附加至 DOM 树,则会有性能的问题。建议在背景准备好节点树片段,等树片段准备好,再将树片段的根节点绑定至 DOM 树,如此会有比较好的性能。
除了自行创建片段之外,也可以使用createDocumentFragment
来创建DocumentFragment
,利用它在背景作树片段组织,再一次将DocumentFragment
附加至 DOM 树。
DOM 元素有个非标准的innerHTML
特性,你可以用之获取标签中内含的 HTML,也可以指定字符串给innerHTML
,浏览器会解析这个字符串,并创建对应的 DOM 元素安插至元素中,过去它不是标准特性,但几乎每个浏览器都支持,而 HTML 5 已将innerHTML
纳入标准。
例如,要在上面提及的<div>
中创建<b>哈啰</b>
,可以如下:
document.getElementById('console').innerHTML = '<b>哈啰</b>';