本文系统介绍JavaScript中DOM元素的核心操作。涵盖节点类型(element、text、comment等)及其属性,节点间的父子兄弟关系,以及appendChild、insertBefore等节点增删改方法。同时解析Document与Element类型的特征,特性操作,以及HTML5新增的选择符API、类名获取和焦点管理功能,帮助开发者掌握DOM编程基础。
节点类型与基本信息
在DOM中,节点类型由nodeType属性标识,用于区分不同类型的节点。常见的nodeType取值包括:值为1表示元素(element),值为2表示属性(attr),值为3表示文本(text),值为8表示注释(comments),以及值为9表示文档(document)。
除了节点类型,nodeName和nodeValue也是获取节点信息的重要属性。
节点关系与遍历
节点之间存在明确的层级关系,每个节点都拥有childNodes属性,该属性保存着一个NodeList对象,包含该节点的所有子节点。
firstChild属性用于获取节点的第一个子节点,其效果等同于访问childNodes列表中的索引0,即childNodes[0]。
lastChild属性用于获取节点的最后一个子节点,其效果等同于访问childNodes列表中的最后一个索引,即childNodes.length-1。
通过遍历childNodes列表中的每个节点,可以利用previousSibling和nextSibling属性来访问同一父节点下的前一个和后一个兄弟节点。
节点增删改操作
操作节点是DOM编程的核心,其中appendChild()是最常用的方法之一。
appendChild()方法用于将一个节点添加到childNodes列表的末尾。执行该操作后,新增节点的parentNode、父节点的最后一个子节点指针以及原最后一个子节点的nextSibling指针都会相应更新,以反映新的DOM结构。
insertBefore()方法允许在指定位置插入节点,它接受两个参数:要插入的新节点和作为参照的现有节点。
// 插入后成为最后一个子节点
returnedNode = someNode.insertBefore(newNode,null);
// 插入后成为第一个节点
returnedNode = someNode.insertBefore(newNode,someNode.firstChild);
// 插入到最后一个子节点前面
returnedNode = someNode.insertBefore(newNode,someNode.lastChild);
replaceChild()方法用于替换现有节点,它接受两个参数:新插入的节点和将被替换的旧节点。
var returnedNode = someNode.replaceChild(newNode,someNode.firstChild);
removeChild()方法仅用于移除节点,不会进行替换。执行后,被移除节点的parentNode将变为null。
var formerFirstChild = someNode.removeChild(someNode.firstChild);
cloneNode()方法用于创建节点的副本,支持深拷贝和浅拷贝两种模式。
示例列表包含item 1、item 2和item 3,常用于演示cloneNode的行为。
var deepList = myList.cloneNode(true);
console.log(deepList.length); // 3
var shallowList = myList.cloneNode(false);
console.log(shallowList.childNodes.length); //0
Document类型详解
Document类型代表了整个HTML或XML文档,是DOM树的根节点。
Document节点具有以下几个显著特征:
- 其nodeType属性值固定为9。
- 其nodeName属性值为字符串"#document"。
- 其nodeValue属性值始终为null。
- 其parentNode属性值为null,因为文档节点没有父节点。
- 其ownerDocument属性值也为null。
文档的子节点通常包括文档类型声明(如果有)和根元素(如<html>)。
var html = document.documentElement; // 取得对<html>的引用
console.log(html === document.childNodes[0]); // true
console.log(html === document.firstChild); // true
Document对象提供了访问文档元数据的方法,如标题、URL和域名等。
// 取得文档的标题
var originalTitle = document.title;
// 设置文档标题
document.title = "New page title";
// 取得完整的url
var url = document.URL;
// 取得域名
var domain = document.domain;
// 取得来源页面的url
var referrer = document.referrer;
//假设页面来自p2p.wrox.com域
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 失败
在IE7及更早版本中,调用document.getElementById("myElement")可能会返回具有该name属性的表单字段,而非ID为"myElement"的元素。为避免此兼容性问题,最佳实践是确保表单字段的name属性值不与任何元素的ID重复。
<input type="text" name="myElement" value="text field">
<div id="myElement">a div</div>
Document类型提供了一些特殊的集合,用于快速访问文档中的特定元素。
- document.anchors集合包含文档中所有带有name属性的<a>元素。
- document.forms集合包含文档中所有的<form>元素,其结果与调用document.getElementsByTagName("form")相同。
- document.images集合包含文档中所有的<img>元素,其结果与调用document.getElementsByTagName("img")相同。
- document.links集合包含文档中所有带有href属性的<a>元素。
Document类型提供了向页面写入内容的方法,如document.write()。
<html>
<head>
<title>document.write() Example 3</title>
</head>
<body>
<script type="text/javascript">
document.write("<script type=\"text/javascript\" src=\"file.js\">") + "<\/script>");
</script>
</body>
</html>
在使用document.write()输出包含</script>的字符串时,浏览器不会将其解析为外部脚本标签的结束标记,从而避免页面中出现多余内容或脚本中断。
Element类型与特性操作
Element类型代表HTML或XML中的元素节点,是DOM结构中最常见的节点类型。
- Element节点的nodeType属性值为1。
- Element节点的nodeName属性值为该元素的标签名(如"div"、"span")。
- Element节点的nodeValue属性值为null。
- Element节点的parentNode可能是Document(如果是根元素)或另一个Element。
要获取元素的标签名,可以使用nodeName属性,也可以使用tagName属性,两者在Element节点上返回相同的结果。
<div id="myDiv"></div>
var div = document.getElementById("myDiv");
console.log(div.tagName); // DIV
console.log(div.nodeName); // DIV
if (element.tagName=="div") { // 不能这样比较,很容易出错
}
if (element.tagName.toLowerCase =="div") { // 这样最好(适用于任何文档)
}
操作元素特性主要涉及三个DOM方法:getAttribute()、setAttribute()和removeAttribute()。需要注意的是,getAttribute()接收的特性名必须与实际特性名完全一致。例如,要获取class属性的值,应传入字符串"class",而不是JavaScript中的className属性名。
var div = document.getElementById("myDiv");
console.log(div.getAttribute("class")); // bd
创建新元素可以使用document.createElement()方法,该方法接收标签名作为参数并返回新的Element节点。
在遍历元素的子节点并执行特定操作前,通常应先检查子节点的nodeType属性,以区分元素节点、文本节点等不同类型的节点。
for (var i=0; len = element.childNodes.length; i<len; i++){
if (element.childNodes[i].nodeType ==1) {
// 执行某些操作
}
}
Text与Comment节点
Text节点代表元素或属性中的文本内容,具有以下特征:
- Text节点的nodeType属性值为3。
- Text节点的nodeName属性值为字符串"#text"。
- Text节点的nodeValue属性值为该节点所包含的文本字符串。
- Text节点的parentNode通常是一个Element节点。
创建新的文本节点可以使用document.createTextNode()方法,该方法接收文本字符串作为参数。
normalize()方法用于将相邻的Text节点合并为一个节点,确保DOM树的规范化。
splitText()方法用于将一个Text节点分割成两个独立的Text节点,分割点由指定的偏移量决定。
Comment节点代表HTML或XML中的注释,具有以下特征:
- Comment节点的nodeType属性值为8。
- Comment节点的nodeName属性值为字符串"#comment"。
- Comment节点的nodeValue属性值为注释的内容字符串。
- Comment节点的parentNode可能是Document或Element。
- Comment节点不支持子节点,即没有子节点。
HTML5 DOM扩展功能
DOM提供了一系列操作表格的技术,主要通过创建table、tbody、tr和td等元素并组合实现。
// 创建 table
var table = document.createElement("table");
table.border = 1;
table.width = "100%";
// 创建tbody
var 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(01);
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);
选择符API中的querySelector()方法允许使用CSS选择器来获取文档中匹配的第一个元素。
// 取得body元素
var tbody = document.querySelector('body');
// 取得ID为"myDIV"的元素
var myDIV = document.querySelector("#myDiv");
// 取得类为"selected"的第一个元素
var selected = document.querySelector(".selected");
// 取得类为"button"的第一个图像元素
var img = document.body.querySelector("img.button");
querySelectorAll()方法允许使用CSS选择器来获取文档中匹配的所有元素,返回一个NodeList。
// 取得某<div>中的所有<em>元素(类似于getElementsByTagName("em"))
var ems = document.getElementById("myDiv").querySelectorAll("em");
// 取得类为"selected"的所有元素
var selecteds = document.querySelectorAll(".selected");
// 取得所有<p>元素中的所有<strong>元素
var strongs = document.querySelectorAll("p strong");
HTML5对类名操作进行了扩充,引入了getElementsByClassName()方法。该方法可以通过document对象及所有HTML元素调用,返回一个包含所有匹配指定类名的元素的实时集合。
// 取得所有类中包含"username"和"current"的元素。类名的先后顺序无所谓
var allCurrentUsernames = document.getElementsByClassName("username current");
// 取得ID为"myDiv"的元素中带有类名"selected"的所有元素
var selected = document.getElementById("myDiv").getElementsByClassName("selected");
焦点管理是DOM交互中的重要环节,HTML5对此进行了增强。
HTML5新增了document.activeElement属性,该属性始终引用当前获得焦点的DOM元素。
var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); // true
默认情况下,当文档加载完成时,document.activeElement指向document.body元素。在文档加载期间,该属性的值为null。此外,HTML5还引入了document.hasFocus()方法,用于判断文档当前是否获得了焦点。
var button = document.getElementById("myButton");
botton.focus();
alert(document.hasFocus()); // true