DOM编程
动态脚本
动态脚本就是在页面初始加载时不存在,之后又通过 DOM 包含的脚本。
有两种方式通过<script>动态为网页添加脚本:
动态加载外部文件
<script src="foo.js"></script>
可以像这样通过 DOM 编程创建这个节点:
let script = document.createElement("script");
script.src = "foo.js";
document.body.appendChild(script); // 动态添加节点
在上面最后一行把<script>元素添加到页面之前,是不会开始下载外部文件的。
动态样式
与动态脚本类似,动态样式也是页面初始加载时并不存在,而是在之后才添加到页面中的。
CSS 样式在 HTML 页面中可以通过两个元素加载:
<link>元素用于包含 CSS 外部文件,而<style>元素用于添加嵌入样式。
第一种:
使用元素包含 CSS 外部文件:
<link rel="stylesheet" type="text/css" href="styles.css">
这个元素很容易使用 DOM 编程创建出来:
let link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "styles.css";
let head = document.getElementsByTagName("head")[0];
head.appendChild(link); // 动态添加样式
以上代码在所有主流浏览器中都能正常运行。注意应该把<link>元素添加到<head>元素而不是
<body>元素,这样才能保证所有浏览器都能正常运行。
第二种
:
使用<script>元素包含嵌入的 CSS 规则,例如:
<style type="text/css">
body {
background-color: red;
}
</style>
逻辑上,下列 DOM 代码会有同样的效果:
let style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode("body{background-color:red}"));
let head = document.getElementsByTagName("head")[0];
head.appendChild(style); // 动态添加样式
操作表格
表格是 HTML 中最复杂的结构之一。通过 DOM 编程创建<table>元素,通常要涉及大量标签,包括表行、表元、表题,等等。因此,通过 DOM 编程创建和修改表格时可能要写很多代码。假设要通过DOM 来创建以下 HTML 表格:
<table border="1" width="100%">
<tbody>
<tr>
<td>Cell 1,1</td>
<td>Cell 2,1</td>
</tr>
<tr>
<td>Cell 1,2</td>
<td>Cell 2,2</td>
</tr>
</tbody>
</table>
下面就是以 DOM 编程方式重建这个表格的代码:
// 创建表格
let table = document.createElement("table");
table.border = 1;
table.width = "100%";
// 创建表体
let tbody = document.createElement("tbody");
table.appendChild(tbody);
// 创建第一行
let row1 = document.createElement("tr");
tbody.appendChild(row1);
let cell1_1 = document.createElement("td");
cell1_1.appendChild(document.createTextNode("Cell 1,1"));
row1.appendChild(cell1_1);
let cell2_1 = document.createElement("td");
cell2_1.appendChild(document.createTextNode("Cell 2,1"));
row1.appendChild(cell2_1);
// 创建第二行
let row2 = document.createElement("tr");
tbody.appendChild(row2);
let cell1_2 = document.createElement("td");
cell1_2.appendChild(document.createTextNode("Cell 1,2"));
row2.appendChild(cell1_2);
let cell2_2= document.createElement("td");
cell2_2.appendChild(document.createTextNode("Cell 2,2"));
row2.appendChild(cell2_2);
// 把表格添加到文档主体
document.body.appendChild(table);
以上代码相当烦琐,也不好理解。为了方便创建表格,HTML DOM 给<table>、<tbody>和<tr>
元素添加了一些属性和方法。
<table>元素添加了以下属性和方法:
caption,指向<caption>元素的指针(如果存在);
tBodies,包含<tbody>元素的 HTMLCollection;
tFoot,指向<tfoot>元素(如果存在);
tHead,指向<thead>元素(如果存在);
rows,包含表示所有行的 HTMLCollection;
createTHead(),创建<thead>元素,放到表格中,返回引用;
createTFoot(),创建<tfoot>元素,放到表格中,返回引用;
createCaption(),创建<caption>元素,放到表格中,返回引用;
deleteTHead(),删除<thead>元素;
deleteTFoot(),删除<tfoot>元素;
deleteCaption(),删除<caption>元素;
deleteRow(pos),删除给定位置的行;
insertRow(pos),在行集合中给定位置插入一行。
<tbody>元素添加了以下属性和方法:
rows,包含<tbody>元素中所有行的 HTMLCollection;
deleteRow(pos),删除给定位置的行;
insertRow(pos)
,在行集合中给定位置插入一行,返回该行的引用。
<tr>元素添加了以下属性和方法:
cells,包含<tr>元素所有表元的 HTMLCollection;
deleteCell(pos),删除给定位置的表元;
insertCell(pos)
,在表元集合给定位置插入一个表元,返回该表元的引用。
使用这些方法重写前面的代码:
// 创建表格
let table = document.createElement("table");
table.border = 1;
table.width = "100%";
// 创建表体
let tbody = document.createElement("tbody");
table.appendChild(tbody);
// 创建第一行
tbody.insertRow(0); // 创建第一行
tbody.rows[0].insertCell(0); // 创建表元并放在第一行第一个位置
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1")); // 引用第一行第一个表元,并添加文本子节点
tbody.rows[0].insertCell(1); // 创建表元并放在第一行第二个位置
tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1")); // 引用第一行第二个表元,并添加文本子节点
// 创建第二行
tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));
// 把表格添加到文档主体
document.body.appendChild(table);
使用NodeList
NodeList 就是基于 DOM 文档的实时查询。例如,下面的代码会导致无穷循环:
let divs = document.getElementsByTagName("div");
for (let i = 0; i < divs.length; ++i){
let div = document.createElement("div");
document.body.appendChild(div);
}
第一行取得了包含文档中所有<div>元素的 HTMLCollection。因为这个集合是“实时的”,所以
任何时候只要向页面中添加一个新<div>元素,再查询这个集合就会多一项。
因为浏览器不希望保存每次创建的集合,所以就会在每次访问时更新集合。这样就会出现前面使用循环的例子中所演示的问题。
每次循环开始,都会求值 i < divs.length。这意味着要执行获取所有<div>元素的查询。因为循环
体中会创建并向文档添加一个新<div>元素,所以每次循环 divs.length 的值也会递增。因为两个值
都会递增,所以 i 将永远不会等于 divs.length
任何时候要迭代 NodeList,最好再初始化一个变量保存当时查询时的长度,然后用循环变量与这
个变量进行比较,如下所示:
let divs = document.getElementsByTagName("div");
for (let i = 0, len = divs.length; i < len; ++i) {
let div = document.createElement("div");
document.body.appendChild(div);
}
在这个例子中,又初始化了一个保存集合长度的变量 len。因为 len 保存着循环开始时集合的长度,而这个值不会随集合增大动态增长,所以就可以避免前面例子中出现的无穷循环。