文档对象模型DOM
文档对象模型 DOM(Document Object Module)是由 W3C 定义的提供与任何 HTML 或 XML 文档进行交互的 API(编程接口),可以说是 HTML 之后的又一伟大创新。它使得用户可以通过 javaScript 访问 HTML 文档中的任意元素和内容,DOM 提供的接口可以操作 HTML 文件中的节点。当浏览器加载一个网页后,这个网页就可以看作是文档树,由多个节点构成。所谓 DOM,就是将 HTML 文档中各个元素按照从属关系建立起的模型。总的来说,我们可以利用 DOM 完成以下应用:
访问指定节点;
访问相关节点;
访问节点属性;
检查节点类型;
创建节点;
为节点添加事件;
操作节点。
HTML 文档与 DOM
我们先看下面这个简单的 HTML 文档:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>一个简单的 HTML 文档</title>
</head>
<body>
<h1>这是一个简单的 HTML 文档</h1>
<p>文档中包含着段落和<a title="link" href="#">超级链接</a></p>
</body>
</html>
这个 HTML 文档的 DOM 可示例如下图:
在上面的 DOM 中,html 位于最顶端,是 DOM 的根节点,head
和 body
是 html
的子节点,它们属于同一层,并不互相包含,是兄弟关系,h1
和 p
是兄弟元素,其父元素是 body
,p
的子元素是 a
元素。
节点
节点(node)的概念来源于计算机网络,它代表网络中的一个连接点。在 DOM 中,文档也是由节点构成的集合。DOM 定义了多种节点,常用的是元素节点、文本节点、和属性节点,分别对应元素、元素中包含的文字内容和元素的属性。
例如上面的代码中:
<a title="link" href="#">超级链接</a>
a
元素的 title
和 href
属性就是 a
元素节点的属性节点,a
元素所包含的内容“超级链接
”就是文本节点。如下图所示:
Plain Text knitr::include_graphics('images/node.png')
使用 DOM
DOM 提供了一种访问文档内容的机制,我们可以使用 DOM 处理 HTML 文档的内容。DOM 由节点(node)构成,每一个节点,都有一系列的属性、方法可以使用,常用属性、方法见下表:
属性/方法 | 类型/返回类型 | 说明 |
---|---|---|
nodeName | String | 节点名称 |
nodeValue | String | 节点的值 |
nodeType | Number | 节点类型 |
firstChild | Node | childNodes 列表的第一个节点 |
lastChild | Node | childNodes 列表的最后一个节点 |
childNodes | NodeList | 所有子节点列表,方法 item(i) 可以访问其中节点 |
parentNode | Node | 父节点 |
previousSibling | Node | 指向前一个兄弟节点 |
nextSibling | Node | 指向后一个兄弟节点 |
hasChildNodes() | Boolean | 是否包含子节点 |
attributes | NameNodeMap | 包含一个元素特性的 Attr 对象 |
appendChild(node) | Node | 将 node 节点添加到 childNodes 末尾 |
removeChild(node) | Node | 从 childNOdes 中删除 node 节点 |
repaceChild(new,old) | Node | 将 childNodes 中的 oldnode 节点替换为 newnode 节点 |
insertBefore(new,ref) | Node | 在 childNOdes 中的 refnode 节点之前插入 newnode 节点 |
innerHTML | String | 读取或者设置某个标记之间的所有内容 |
className | String | 读取或者设置节点的 CSS 类别 |
访问节点
DOM 提供了一些很便捷的方法来访问文档的节点,最常用的是 getElementsByTagName()
和getElementById()
,此外比较常用的是 getElementsByName()
、getElementsByClassName()
、Document.querySelector()
等等。
……var oLi = document.getElementsByTagName('li');
for(var i = 0 in oLi) {
console.log(i + '=' + oLi[i]);
}console.log(oLi);
console.log(oLi.length);
console.log(oLi[0].childNodes[0].nodeValue);
……
将访问 oLi
子节点列表中的第一个节点的值。
检测节点类型
通过节点的 nodeType
属性可以检测出节点的类型,DOM 定义了 12 种类型,大多数情况下,我们用到的是以下类型:
ELEMENT_NODE
元素节点 nodeType 的值为 1;ATTRIBUTE_NODE
属性节点 nodeType 的值为 2;TEXT_NODE
文本节点 nodeType 的值为 3;
利用父子兄关系查找节点
在获取了某个节点之后,可以通过父子关系,利用 hasChildNodes()
方法和 childNodes
属性获取该节点所包含的所有子节点。例如:
……var oLi = document.getElementById('myList');
var DOMString = "";
if(oLi.hasChildNodes()){
var oChild = oLi.childNodes;
for(var i=0; i<oChild.length;i++){
DOMString += oChild[i].nodeName + "\n";
}
}console.log(DOMString);
……
从运行结果我们可以看出,不光有元素节点,连它们之间的空格也被当成了子节点。
通过 parentNode
属性,可以获取父元素节点。如:
……var myItem = document.getElementById('active');
console.log(myItem.parentNode.tagName);
……
DOM 还提供了处理兄弟之间关系的属性和方法,如 nextSibling
、previousSibling
。
设置节点属性
找到节点后,可以通过 getAttribute()
、setAttribute()
方法取得或者设定节点的属性。
……var myItem = document.getElementById('active');
console.log(myItem.parentNode.tagName);
console.log(myItem.getAttribute("title"));
.setAttribute("title","主要用以和用户交互");
myItemconsole.log(myItem.getAttribute("title"));
……
创建和添加节点
创建元素节点采用 creatElment()
, 创建文本节点采用 creatTextNode()
, 创建文档碎片节点采用 creatDocumentFragment()
等等。
在插入元素之前,先将元素内容添加到元素节点,再将元素节点及其包含的文本节点添加到指定节点。
……var oP = document.createElement("p");
var oText = document.createTextNode("这是一个使用 DOM 生成的段落");
.appendChild(oText);
oPdocument.body.appendChild(oP);
……
删除节点
删除节点使用 removeChild()
方法,通常先找到要删除的节点,然后利用 parentNode
属性找到父节点,然后使用父节点的 removeChild
方法。
……<body onload="removeLi()">
<ul id="myList">
<li>HTML</li>
<li>CSS</li>
<li id="active" title="JavaScript 是脚本编程语言">JavaScript</li>
</ul>
<script>
function removeLi() {
var oLi = document.getElementById('active');
.parentNode.removeChild(oLi);
oLi
}</script>
</body>
……
替换节点
DOM 提供 replaceChild()
方法来替换节点,具体过程和删除节点类似,先找到想要替换的节点,再创建新节点,然后使用父节点替换方法。
……<body onload="replaceLi()">
<ul id="myList">
<li>HTML</li>
<li>CSS</li>
<li id="active">JavaScript</li>
</ul>
<script>
function replaceLi() {
var oOldLi = document.getElementById('active');
var oNewLi = document.createElement('li');
var oText = document.createTextNode('ECMAScript');
.appendChild(oText);
oNewLi.parentNode.replaceChild(oNewLi,oOldLi);
oOldLi
}</script>
</body>
……
innerHTML 属性
innerHTML
属性虽然不是 W3C DOM 的组成部分,但它得到了目前主流浏览器的支持。该属性表示某个标记之间的所有内容,包括代码本身。
var oLi = document.getElementById('active');
console.log(oLi.innerHTML);
.innerHTML = "<a href='http://www.baidu.com'>Baidu</a>"; oLi
className 属性
className 属性是一个非常实用的属性,可以修改一个节点的 CSS 类别。
var oLi = document.getElementById('active');
console.log(oLi.className);
.className += ' jsdemo';
oLiconsole.log(oLi.className);
使用 className
属性如果要追加 CSS 样式,而不是覆盖 CSS 样式的话,使用“+=
”连接运算符,另外注意不同的样式名称使用空格分割。