构建和更新 DOM 树
本文概述了一些强大的、基础的 DOM Level 1 方法以及如何从 JavaScript 中使用它们。您将学习如何动态创建、访问、控制和删除 HTML 元素。此处介绍的 DOM 方法并非仅限于 HTML;它们也适用于 XML。此处提供的演示在任何现代浏览器中都能正常工作。
注意:此处介绍的 DOM 方法是 Document Object Model (Core) Level 1 规范的一部分。DOM Level 1 包括用于通用文档访问和操作的方法 (DOM 1 Core) 以及特定于 HTML 文档的方法 (DOM 1 HTML)。
动态创建 HTML 表格
示例
在此示例中,当点击按钮时,我们向页面添加一个新表格。
HTML
<input type="button" value="Generate a table" />
JavaScript
function generateTable() {
// creates a <table> element and a <tbody> element
const tbl = document.createElement("table");
const tblBody = document.createElement("tbody");
// creating all cells
for (let i = 0; i < 2; i++) {
// creates a table row
const row = document.createElement("tr");
for (let j = 0; j < 2; j++) {
// Create a <td> element and a text node, make the text
// node the contents of the <td>, and put the <td> at
// the end of the table row
const cell = document.createElement("td");
const cellText = document.createTextNode(`cell in row ${i}, column ${j}`);
cell.appendChild(cellText);
row.appendChild(cell);
}
// add the row to the end of the table body
tblBody.appendChild(row);
}
// put the <tbody> in the <table>
tbl.appendChild(tblBody);
// appends <table> into <body>
document.body.appendChild(tbl);
// sets the border attribute of tbl to '2'
tbl.setAttribute("border", "2");
}
document
.querySelector("input[type='button']")
.addEventListener("click", generateTable);
结果
解释
注意我们创建元素和文本节点时的顺序
- 首先,我们创建了
<table>
元素。 - 接下来,我们创建了
<tbody>
元素,它是<table>
元素的子元素。 - 接下来,我们使用循环创建
<tr>
元素,它们是<tbody>
元素的子元素。 - 对于每个
<tr>
元素,我们使用循环创建<td>
元素,它们是<tr>
元素的子元素。 - 对于每个
<td>
元素,我们创建了包含表格单元格文本的文本节点。
一旦我们创建了 <table>
、<tbody>
、<tr>
和 <td>
元素,然后创建了文本节点,我们就以相反的顺序将每个对象附加到其父节点。
-
首先,我们使用以下方法将每个文本节点附加到其父
<td>
元素:jscell.appendChild(cellText);
-
接下来,我们使用以下方法将每个
<td>
元素附加到其父<tr>
元素:jsrow.appendChild(cell);
-
接下来,我们使用以下方法将每个
<tr>
元素附加到父<tbody>
元素:jstblBody.appendChild(row);
-
接下来,我们使用以下方法将
<tbody>
元素附加到其父<table>
元素:jstbl.appendChild(tblBody);
-
接下来,我们使用以下方法将
<table>
元素附加到其父<body>
元素:jsdocument.body.appendChild(tbl);
记住这个技巧。在 W3C DOM 编程中,您会经常使用它。首先,您从上到下创建元素;然后,您从下到上将子节点附加到父节点。
这是 JavaScript 代码生成的 HTML 标记
<table border="2">
<tbody>
<tr>
<td>cell is row 0 column 0</td>
<td>cell is row 0 column 1</td>
</tr>
<tr>
<td>cell is row 1 column 0</td>
<td>cell is row 1 column 1</td>
</tr>
</tbody>
</table>
这是代码生成的 <table>
元素及其子元素的 DOM 对象树
您可以使用一些 DOM 方法来构建此表格及其内部子元素。请记住您要创建的结构的树模型;这将使编写必要的代码更容易。在图 1 的 <table>
树中,<table>
元素有一个子元素:<tbody>
元素。<tbody>
有两个子元素。每个 <tbody>
的子元素 (<tr>
) 有两个子元素 (<td>
)。最后,每个 <td>
有一个子元素:一个文本节点。
设置段落的背景颜色
示例
在此示例中,当点击按钮时,我们更改段落的背景颜色。
HTML
<body>
<input type="button" value="Set paragraph background color" />
<p>hi</p>
<p>hello</p>
</body>
JavaScript
function setBackground() {
// now, get all the p elements in the document
const paragraphs = document.getElementsByTagName("p");
// get the second paragraph from the list
const secondParagraph = paragraphs[1];
// set the inline style
secondParagraph.style.background = "red";
}
document.querySelector("input").addEventListener("click", setBackground);
结果
解释
getElementsByTagName(tagNameValue)
是任何 DOM Element
或根 Document
元素都可用的方法。调用时,它会返回一个包含所有匹配该标签名的元素后代的数组。列表的第一个元素位于数组的位置 [0]
。
我们执行了以下步骤
使用 document.createTextNode("..") 创建 TextNodes
使用 document 对象调用 createTextNode
方法并创建您的文本节点。您只需要传递文本内容。返回值是一个代表文本节点的对象。
myTextNode = document.createTextNode("world");
这意味着您创建了一个类型为 TEXT_NODE
(一段文本) 的节点,其文本数据为 "world"
,而 myTextNode
是指向此节点对象的引用。要将此文本插入到您的 HTML 页面中,您需要将此文本节点作为另一个节点元素的子节点。
使用 appendChild(..) 插入元素
因此,通过调用 secondParagraph.appendChild(node_element)
,您将该元素作为第二个 <p>
元素的新的子元素。
secondParagraph.appendChild(myTextNode);
测试完此示例后,请注意单词 hello 和 world 在一起:helloworld。因此,在视觉上,当您看到 HTML 页面时,似乎两个文本节点 hello 和 world 是一个节点,但请记住,在文档模型中,有两个节点。第二个节点是类型为 TEXT_NODE
的新节点,它是第二个 <p>
标签的第二个子节点。下图显示了最近创建的 Text Node 对象在文档树中的位置。
注意: createTextNode()
和 appendChild()
是在单词 hello 和 world 之间包含空格的简单方法。另一个重要注意事项是,appendChild
方法会在最后一个子节点之后附加子节点,就像 world 这个词被添加到 hello 之后一样。所以,如果您想在 hello 和 world 之间附加一个文本节点,您需要使用 insertBefore
而不是 appendChild
。
使用 document 对象和 createElement(..) 方法创建新元素
您可以使用 createElement
创建新的 HTML 元素或任何您想要的元素。例如,如果您想创建一个新的 <p>
元素作为 <body>
元素的子元素,您可以使用上一个示例中的 myBody
并附加一个新元素节点。要创建一个节点,请调用 document.createElement("tagname")
。例如:
myNewPTagNode = document.createElement("p");
myBody.appendChild(myNewPTagNode);
使用 removeChild(..) 方法删除节点
节点可以被删除。以下代码会从第二个 <p>
元素 secondParagraph
中删除文本节点 myTextNode
(包含单词 "world")。
secondParagraph.removeChild(myTextNode);
文本节点 myTextNode
(包含单词 "world")仍然存在。以下代码将 myTextNode
附加到最近创建的 <p>
元素 myNewPTagNode
。
myNewPTagNode.appendChild(myTextNode);
修改后的对象树的最终状态如下所示
动态创建表格
下图显示了示例中创建的表格的表格对象树结构。
回顾 HTML 表格结构
创建元素节点并将它们插入到文档树中
创建表格的基本步骤是:
- 获取 body 对象(document 对象的第一个项)。
- 创建所有元素。
- 最后,根据表格结构(如上图所示)附加每个子节点。
注意:在脚本的最后,有一行新代码。表格的 border
属性是使用另一个 DOM 方法 setAttribute()
设置的。setAttribute()
有两个参数:属性名和属性值。您可以使用 setAttribute
方法设置任何元素的任何属性。
// get the reference for the body
const myBody = document.getElementsByTagName("body")[0];
// creates <table> and <tbody> elements
const myTable = document.createElement("table");
const myTableBody = document.createElement("tbody");
// creating all cells
for (let j = 0; j < 3; j++) {
// creates a <tr> element
const myCurrentRow = document.createElement("tr");
for (let i = 0; i < 4; i++) {
// creates a <td> element
const myCurrentCell = document.createElement("td");
// creates a Text Node
const currentText = document.createTextNode(
`cell is row ${j}, column ${i}`,
);
// appends the Text Node we created into the cell <td>
myCurrentCell.appendChild(currentText);
// appends the cell <td> into the row <tr>
myCurrentRow.appendChild(myCurrentCell);
}
// appends the row <tr> into <tbody>
myTableBody.appendChild(myCurrentRow);
}
// appends <tbody> into <table>
myTable.appendChild(myTableBody);
// appends <table> into <body>
myBody.appendChild(myTable);
// sets the border attribute of myTable to 2;
myTable.setAttribute("border", "2");
使用 DOM 和 CSS 操作表格
从表格中获取文本节点
此示例引入了两个新的 DOM 属性。首先,它使用 childNodes
属性获取 myCell 的子节点列表。childNodes
列表包括所有子节点,无论它们的名称或类型是什么。与 getElementsByTagName()
类似,它返回一个节点列表。
区别在于 (a) getElementsByTagName()
只返回指定标签名的元素;以及 (b) childNodes
包括任何级别的所有后代,而不仅仅是直接子节点。
一旦您获得了返回的列表,就可以使用 [x]
方法来检索所需的子项。此示例将表格第二行第二个单元格的文本节点存储在 myCellText
中。
然后,为了在此示例中显示结果,它会创建一个新的文本节点,其内容是 myCellText
的数据,并将其作为 <body>
元素的子节点附加。
注意:如果您的对象是文本节点,您可以使用 data 属性并检索节点的文本内容。
const myBody = document.getElementsByTagName("body")[0];
const myTable = myBody.getElementsByTagName("table")[0];
const myTableBody = myTable.getElementsByTagName("tbody")[0];
const myRow = myTableBody.getElementsByTagName("tr")[1];
const myCell = myRow.getElementsByTagName("td")[1];
// first item element of the childNodes list of myCell
const myCellText = myCell.childNodes[0];
// content of currentText is the data content of myCellText
const currentText = document.createTextNode(myCellText.data);
myBody.appendChild(currentText);
获取属性值
在 sample1 的末尾,有一个对 myTable
对象调用 setAttribute
的地方。此调用用于设置表格的 border 属性。要检索属性的值,请使用 getAttribute
方法。
myTable.getAttribute("border");
通过更改样式属性隐藏列
一旦您将对象放入 JavaScript 变量中,您就可以直接设置 style
属性。以下代码是一个修改版本,其中隐藏了第二列的每个单元格,并将第一列的每个单元格的背景更改为红色。请注意,style
属性是直接设置的。
const myBody = document.getElementsByTagName("body")[0];
const myTable = document.createElement("table");
const myTableBody = document.createElement("tbody");
for (let row = 0; row < 2; row++) {
const myCurrentRow = document.createElement("tr");
for (let col = 0; col < 2; col++) {
const myCurrentCell = document.createElement("td");
const currentText = document.createTextNode(`cell is: ${row}${col}`);
myCurrentCell.appendChild(currentText);
myCurrentRow.appendChild(myCurrentCell);
// set the cell background color
// if the column is 0. If the column is 1 hide the cell
if (col === 0) {
myCurrentCell.style.background = "red";
} else {
myCurrentCell.style.display = "none";
}
}
myTableBody.appendChild(myCurrentRow);
}
myTable.appendChild(myTableBody);
myBody.appendChild(myTable);