文档对象模型DOM

前端
JavaScript
作者

yangjh

发布日期

November 14, 2023

文档对象模型 DOM(Document Object Module)是由 W3C 定义的提供与任何 HTML 或 XML 文档进行交互的 API(编程接口),可以说是 HTML 之后的又一伟大创新。它使得用户可以通过 javaScript 访问 HTML 文档中的任意元素和内容,DOM 提供的接口可以操作 HTML 文件中的节点。当浏览器加载一个网页后,这个网页就可以看作是文档树,由多个节点构成。所谓 DOM,就是将 HTML 文档中各个元素按照从属关系建立起的模型。总的来说,我们可以利用 DOM 完成以下应用:

  1. 访问指定节点;

  2. 访问相关节点;

  3. 访问节点属性;

  4. 检查节点类型;

  5. 创建节点;

  6. 为节点添加事件;

  7. 操作节点。

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 的根节点,headbodyhtml 的子节点,它们属于同一层,并不互相包含,是兄弟关系,h1p 是兄弟元素,其父元素是 bodyp 的子元素是 a 元素。

节点

节点(node)的概念来源于计算机网络,它代表网络中的一个连接点。在 DOM 中,文档也是由节点构成的集合。DOM 定义了多种节点,常用的是元素节点、文本节点、和属性节点,分别对应元素、元素中包含的文字内容和元素的属性。

例如上面的代码中:

<a title="link" href="#">超级链接</a>

a 元素的 titlehref 属性就是 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 种类型,大多数情况下,我们用到的是以下类型:

  1. ELEMENT_NODE 元素节点 nodeType 的值为 1;

  2. ATTRIBUTE_NODE 属性节点 nodeType 的值为 2;

  3. 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 还提供了处理兄弟之间关系的属性和方法,如 nextSiblingpreviousSibling

设置节点属性

找到节点后,可以通过 getAttribute()setAttribute() 方法取得或者设定节点的属性。

……
var myItem = document.getElementById('active');
console.log(myItem.parentNode.tagName);
console.log(myItem.getAttribute("title"));
myItem.setAttribute("title","主要用以和用户交互");
console.log(myItem.getAttribute("title"));
……

创建和添加节点

创建元素节点采用 creatElment(), 创建文本节点采用 creatTextNode(), 创建文档碎片节点采用 creatDocumentFragment() 等等。

在插入元素之前,先将元素内容添加到元素节点,再将元素节点及其包含的文本节点添加到指定节点。

……
var oP = document.createElement("p");
var oText = document.createTextNode("这是一个使用 DOM 生成的段落");
oP.appendChild(oText);
document.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');
        oLi.parentNode.removeChild(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');
        oNewLi.appendChild(oText);
        oOldLi.parentNode.replaceChild(oNewLi,oOldLi);
    }
    </script>
</body>
……

innerHTML 属性

innerHTML 属性虽然不是 W3C DOM 的组成部分,但它得到了目前主流浏览器的支持。该属性表示某个标记之间的所有内容,包括代码本身。

var oLi = document.getElementById('active');
console.log(oLi.innerHTML);
oLi.innerHTML = "<a href='http://www.baidu.com'>Baidu</a>";

className 属性

className 属性是一个非常实用的属性,可以修改一个节点的 CSS 类别。

var oLi = document.getElementById('active');
console.log(oLi.className);
oLi.className += ' jsdemo';
console.log(oLi.className);

使用 className 属性如果要追加 CSS 样式,而不是覆盖 CSS 样式的话,使用“+=”连接运算符,另外注意不同的样式名称使用空格分割。

回到顶部