Skip to content

DOM 基础

The Document Object Model (DOM) connects web pages to scripts or programming languages by representing the structure of a document — such as the HTML representing a web page — in memory. Usually it refers to JavaScript, even though modeling HTML, SVG, or XML documents as objects are not part of the core JavaScript language.

The DOM represents a document with a logical tree. Each branch of the tree ends in a node, and each node contains objects. DOM methods allow programmatic access to the tree. With them, you can change the document's structure, style, or content.

Nodes can also have event handlers attached to them. Once an event is triggered, the event handlers get executed.

什么是 DOM

  • DOM,全称是“Document Object Model(文档对象模型)”,它是由 W3C 定义的一个标准
  • DOM 操作,可以简单地理解成“元素操作”
  • DOM 采用的是“树型结构”,用“树节点”的形式来表面页面中的一个元素
  • 我们在操作元素时,其实就是把这个元素看成一个对象,然后使用这个对象的属性和方法来进行相关操作

节点类型

DOM 节点共有 12 种类型,只需掌握三种:

  1. 元素节点
  2. 属性节点
  3. 文本节点

JavaScript 会把元素、属性以及文本当作不同的节点来处理。表示元素的叫作“元素节点”,表示属性的叫作“属性节点”,表示文本的叫作“文本节点”。很多人认为节点就等于元素,这样的理解是错的,因为节点有好多种。总而言之,节点和元素是不一样的概念,节点是包括元素的(即节点是比元素高了一个层级的概念)。

在 JavaScript 中,我们可以使用 nodeType 属性来判断一个节点的类型。不同节点的 nodeType 属性值如表 9-1 所示。

表 9-1 不同节点的 nodeType 属性值

节点类型nodeType 值
元素节点1
属性节点2
文本节点3

nodeType 的值是一个数字,而不是像“element”或“attribute”那样的英文字符串。

此外,对于节点类型,需要特别注意以下 3 点:

  • 一个元素就是一个节点,这个节点称为“元素节点”
  • 属性节点和文本节点看起来像是元素节点的一部分,但实际上,它们是独立的节点,并不属于元素节点
  • 只有元素节点才可以拥有子节点,属性节点和文本节点都无法拥有子节点

获取元素

获取元素 6 种方式:

  1. getElementById() 获取的是一个 DOM 对象,我们在给变量命名的时候,习惯性地以英文“o”开头,以便跟其他变量区分开,这可以让我们一眼就看出这是一个 DOM 对象
  2. getElementsByTagName(),获取的是多个元素(即集合),注意有 s,返回一个“类数组”(也叫伪数组),只能用下标和 length,数组的其他方法不能用
  3. getElementsByClassName(),也有 s,获取的也是一个“类数组”
  4. querySelector("选择器"),表示选取满足选择条件的第 1 个元素;querySelectorAll() 表示选取满足条件的所有元素,获取的是多个元素,因此这也是一个“类数组”
  5. getElementsByName(),只用于表单元素,一般只用于单选按钮和复选框
  6. document.titledocument.body

实际上,getElementById()getElementsByTagName() 有以下 3 个明显的区别(很重要,认真理解):

  • getElementById() 获取的是 1 个元素,而 getElementsByTagName() 获取的是多个元素(伪数组)
  • getElementById() 前面只可以接 document,也就是 document.getElementById()getElementsByTagName() 前面不仅可以接 document,还可以接其他 DOM 对象
  • getElementById() 不可以操作动态创建的 DOM 元素,而 getElementsByTagName() 可以操作动态创建的 DOM 元素

创建元素

在 JavaScript 中,我们可以使用 createElement() 来创建一个元素节点,也可以使用 createTextNode() 来创建一个文本节点,然后可以将元素节点与文本节点“组装”成我们平常看到的“有文本内容的元素”。

这种方式又被称为“动态 DOM 操作”。所谓的“动态 DOM”,指的是使用 JavaScript 创建的元素。这个元素一开始在 HTML 中是不存在的。

语法

js
var e1 = document.createElement("元素名"); // 创建元素节点
var txt = document.createTextNode("文本内容"); // 创建文本节点
e1.appendChild(txt); // 把文本节点插入元素节点中
e2.appendChild(e1); // 把组装好的元素 e1 插入已存在的元素 e2 中
  1. 创建简单元素(不带属性)
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oDiv = document.getElementById("content");
        var oStrong = document.createElement("strong");
        var oTxt = document.createTextNode("绿叶学习网");
        // 将文本节点插入 strong 元素
        oStrong.appendChild(oTxt);
        // 将 strong 元素插入 div 元素(这个 div 在 HTML 已经存在)
        oDiv.appendChild(oStrong);
      };
    </script>
  </head>
  <body>
    <div id="content"></div>
  </body>
</html>
  1. 创建复杂元素(带属性)
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oInput = document.createElement("input");
        oInput.id = "submit";
        oInput.type = "button";
        oInput.value = "提交";
        document.body.appendChild(oInput);
      };
    </script>
  </head>
  <body></body>
</html>
  1. 动态创建图片
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oImg = document.createElement("img");
        oImg.className = "pic";
        oImg.src = "img/haizei.png";
        oImg.style.border = "1px solid silver";
        document.body.appendChild(oImg);
      };
    </script>
  </head>
  <body></body>
</html>
  1. 创建多个元素
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <style type="text/css">
      table {
        border-collapse: collapse;
      }
      tr,
      td {
        width: 80px;
        height: 20px;
        border: 1px solid gray;
      }
    </style>
    <script>
      window.onload = function () {
        // 动态创建表格
        var oTable = document.createElement("table");
        for (var i = 0; i < 3; i++) {
          var oTr = document.createElement("tr");
          for (var j = 0; j < 3; j++) {
            var oTd = document.createElement("td");
            oTr.appendChild(oTd);
          }
          oTable.appendChild(oTr);
        }
        // 添加到 body 中去
        document.body.appendChild(oTable);
      };
    </script>
  </head>
  <body></body>
</html>

根据上面的几个例子,我们可以总结一下,想要创建一个元素,需要以下 4 步:

  1. 创建元素节点:createElement()
  2. 创建文本节点:createTextNode()
  3. 把文本节点插入元素节点:appendChild()
  4. 把组装好的元素插入到已有元素中:appendChild()

创建方法前面只可以接 document,插入方法前面不仅可以接 document,还可以接其他 DOM 对象。

插入元素

  • A.appendChild(b),让 b 成为 A 的子节点,插到末尾
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oBtn = document.getElementById("btn");
        // 为按钮添加点击事件
        oBtn.onclick = function () {
          var oUl = document.getElementById("list");
          var oTxt = document.getElementById("txt");
          // 将文本框的内容转换为"文本节点"
          var textNode = document.createTextNode(oTxt.value);
          // 动态创建一个 li 元素
          var oLi = document.createElement("li");
          //将文本节点插入 li 元素中去
          oLi.appendChild(textNode);
          //将 li 元素插入 ul 元素中去
          oUl.appendChild(oLi);
        };
      };
    </script>
  </head>
  <body>
    <ul id="list">
      <li>HTML</li>
      <li>CSS</li>
      <li>JavaScript</li>
    </ul>
    <input id="txt" type="text" /><input id="btn" type="button" value="插入" />
  </body>
</html>
  • A.insertBefore(b, ref),在 ref 前面插入 b
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oBtn = document.getElementById("btn");
        // 为按钮添加点击事件
        oBtn.onclick = function () {
          var oUl = document.getElementById("list");
          var oTxt = document.getElementById("txt");
          // 将文本框的内容转换为"文本节点"
          var textNode = document.createTextNode(oTxt.value);
          // 动态创建一个 li 元素
          var oLi = document.createElement("li");
          //将文本节点插入 li 元素中去
          oLi.appendChild(textNode);
          //将 li 元素插入到 ul 的第 1 个子元素前面
          oUl.insertBefore(oLi, oUl.firstElementChild);
        };
      };
    </script>
  </head>
  <body>
    <ul id="list">
      <li>HTML</li>
      <li>CSS</li>
      <li>JavaScript</li>
    </ul>
    <input id="txt" type="text" /><input id="btn" type="button" value="插入" />
  </body>
</html>

问题

浏览器用户仅仅能够“动态地”在本地网页上添加、修改无序列表的内容,但是无法修改服务器站点上的原始页面内容?

删除元素

  • A.removeChild(b)
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oBtn = document.getElementById("btn");
        oBtn.onclick = function () {
          var oUl = document.getElementById("list");
          // 删除最后一个子元素
          oUl.removeChild(oUl.lastElementChild);
        };
      };
    </script>
  </head>
  <body>
    <ul id="list">
      <li>HTML</li>
      <li>CSS</li>
      <li>JavaScript</li>
      <li>jQuery</li>
      <li>Vue.js</li>
    </ul>
    <input id="btn" type="button" value="删除" />
  </body>
</html>

在使用 removeChild() 方法删除元素之前,我们必须找到以下 2 个元素:

  • 被删除的子元素
  • 被删除子元素的父元素

复制元素

  • obj.cloneNode(bool)
  • 1true:复制元素本身以及所有子元素
  • 0false:仅复制元素本身,不复制子元素
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oBtn = document.getElementById("btn");
        oBtn.onclick = function () {
          var oUl = document.getElementById("list");
          document.body.appendChild(oUl.cloneNode(1));
        };
      };
    </script>
  </head>
  <body>
    <ul id="list">
      <li>HTML</li>
      <li>CSS</li>
      <li>JavaScript</li>
    </ul>
    <input id="btn" type="button" value="复制" />
  </body>
</html>

替换元素

  • A.replaceChild(new, old)
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oBtn = document.getElementById("btn");
        oBtn.onclick = function () {
          // 获取 body 中的第 1 个元素
          var oFirst = document.querySelector("body *: first-child");
          // 获取 2 个文本框
          var oTag = document.getElementById("tag");
          var oTxt = document.getElementById("txt");
          // 根据 2 个文本框的值来创建一个新节点
          var oNewTag = document.createElement(oTag.value);
          var oNewTxt = document.createTextNode(oTxt.value);
          oNewTag.appendChild(oNewTxt);
          document.body.replaceChild(oNewTag, oFirst);
        };
      };
    </script>
  </head>
  <body>
    <p>JavaScript</p>
    <hr />
    输入标签:<input id="tag" type="text" /><br />
    输入内容:<input id="txt" type="text" /><br />
    <input id="btn" type="button" value="替换" />
  </body>
</html>

想要实现替换元素,必须提供 3 个节点:

  • 父元素
  • 新元素
  • 旧元素

WARNING

关于替换元素的这段代码,必须在浏览器内演示才可以实现“替换”功能,在 CodePen 或者 VS Code 右边栏预览,均无法实现“替换”功能。

最近更新