网站

JavaScript HTML DOM

文档对象模型 (DOM) | MDN

Node | MDN

引入

JS 需要操作网页内容和结构。但 JS 不能直接读 HTML 源码,浏览器提供了一套接口,把 HTML 转成 JS 可以操作的对象结构,这就是 DOM。

DOM 把网页转成一棵”树”,JS 通过这棵树可以访问和修改页面上的任何内容。

正文

定义

定义

DOM(Document Object Model)是浏览器把 HTML 文档转换成的对象模型。整个页面被表示为一棵树,每个标签、文本、注释都是树上的一个节点。

简单说:DOM 是 JS 操作页面的桥梁。没有 DOM,JS 就接触不到 HTML。

DOM 树

浏览器读取 HTML 后,会把标签按照嵌套关系组织成一棵树。

<!DOCTYPE html>
<html>
  <head>
    <title>页面标题</title>
  </head>
  <body>
    <h1>标题</h1>
    <p>一段文字</p>
  </body>
</html>

转换后的 DOM 树结构大致如下:

document
 └── html
      ├── head
      │    └── title
      │         └── "页面标题"
      └── body
           ├── h1
           │    └── "标题"
           └── p
                └── "一段文字"

document 是整棵树的根节点,html 是它的子节点,headbodyhtml 的子节点,依此类推。

  • 外层标签是内层标签的父节点
  • 内层标签是外层标签的子节点
  • 同一层级的标签互为兄弟节点

节点类型

DOM 树上的每个组成部分都叫节点,但节点的类型不同。

常见的三种节点:

节点类型说明示例
元素节点HTML 标签本身<h1><p><div>
文本节点标签里的文字内容"标题""一段文字"
注释节点HTML 注释<!-- 这是一个注释 -->

元素节点是最常操作的节点类型。

<p>一段文字</p>

这里 <p> 是元素节点,"一段文字"<p> 里面的文本节点。

属性(例如 classidsrc)不属于独立的子节点,它们挂在元素节点上。

nodeType

nodeType 用来判断一个节点属于什么类型,返回一个数字。处理 firstChildchildNodes 这类可能包含文本节点的结果时,它很有用。

<p id="text">一段文字</p>
const text = document.querySelector("#text");
 
console.log(text.nodeType); // 1,元素节点
console.log(text.firstChild.nodeType); // 3,文本节点

常见的 nodeType 值:

常量节点类型
1Node.ELEMENT_NODE元素节点
3Node.TEXT_NODE文本节点
8Node.COMMENT_NODE注释节点
9Node.DOCUMENT_NODEdocument 节点

判断时优先使用 Node.ELEMENT_NODE 这类常量,比直接记数字更容易理解。

if (text.nodeType === Node.ELEMENT_NODE) {
  console.log("这是元素节点");
}

子节点访问

firstChildlastChild 分别获取一个节点的第一个、最后一个子节点。它们属于 Node 接口,所以元素节点、文本节点和 document 等节点都可能使用。

<ul id="list"><li>HTML</li><li>CSS</li></ul>
const list = document.querySelector("#list");
 
console.log(list.firstChild); // 第一个 <li>
console.log(list.lastChild); // 最后一个 <li>

firstChildlastChild 会把文本节点也算进去。HTML 换行和缩进产生的空白,通常也会成为文本节点。

<ul id="list">
  <li>HTML</li>
  <li>CSS</li>
</ul>
const list = document.querySelector("#list");
 
console.log(list.firstChild.nodeType); // 3,换行产生的文本节点
console.log(list.firstElementChild); // 第一个 <li>
console.log(list.lastElementChild); // 最后一个 <li>

只需要处理 HTML 标签时,优先使用 firstElementChildlastElementChildchildren,可以避开空白文本节点。

元素对象

HTML 里的每个标签在 DOM 中都是一个元素对象。

JS 获取到这个元素对象后,就可以读取或修改它的信息。

<p class="text">旧内容</p>
const text = document.querySelector(".text");
 
console.log(text.tagName);
console.log(text.textContent);
console.log(text.className);

输出:

P
旧内容
text
  • tagName:元素的标签名(大写)
  • textContent:元素的文本内容
  • className:元素的 class 属性值

元素对象上还有很多属性和方法,例如 stylechildreninnerHTMLsetAttribute()addEventListener() 等。

document 对象

document 是 DOM 操作的入口,代表整个 HTML 文档。

console.log(document);

document 对象本身也是一个节点,它是整棵 DOM 树的根。

常用入口属性:

属性/方法作用
document.body获取 <body> 元素
document.head获取 <head> 元素
document.title获取或设置页面标题
document.querySelector()查找第一个匹配元素
document.querySelectorAll()查找所有匹配元素
document.createElement()创建一个新元素
document.createTextNode()创建一个新文本节点
console.log(document.title);
 
document.title = "新标题";

document 是一切 DOM 操作的起点。

DOM 和 HTML 的关系

HTML 是源码,DOM 是浏览器解析后生成的对象结构。

JS 修改 DOM 时,浏览器会实时更新页面显示。

<p class="text">旧内容</p>
const text = document.querySelector(".text");
 
text.textContent = "新内容";

JS 修改了 textContent 后,页面上的文字也会立刻变成”新内容”。

浏览器不会修改原始的 HTML 文件,只改变内存中的 DOM 和页面显示。

常见写法

写法作用
document.querySelector(".text")获取第一个 classtext 的元素
document.querySelectorAll("p")获取所有 <p> 元素
element.textContent读取或修改元素文本
element.style.color = "red"修改元素的样式
document.createElement("div")创建一个 <div> 元素
parent.appendChild(child)向父元素添加一个子元素
element.addEventListener("click", fn)给元素绑定点击事件
element.remove()从页面中移除元素
node.nodeType获取节点类型
node.firstChild获取第一个子节点,可能是文本节点
node.lastChild获取最后一个子节点,可能是文本节点
element.firstElementChild获取第一个元素子节点

例子

获取元素并修改内容

<h1 id="title">旧标题</h1>
const title = document.querySelector("#title");
 
title.textContent = "新标题";

先通过 document.querySelector() 获取 <h1> 元素对象,再修改它的 textContent

读取输入框内容

<input id="userName" type="text">
<button id="btn">提交</button>
const userName = document.querySelector("#userName");
const btn = document.querySelector("#btn");
 
btn.addEventListener("click", function () {
  console.log(userName.value);
});

点击按钮时,通过 value 取出输入框当前的内容。

遍历多个元素

<li class="item">HTML</li>
<li class="item">CSS</li>
<li class="item">JS</li>
const items = document.querySelectorAll(".item");
 
items.forEach(function (item) {
  console.log(item.textContent);
});

获取所有 .item 元素后,用 forEach() 遍历,依次输出每个元素的文本。

特点

  • DOM 是 HTML 文档的对象表示,是 JS 操作页面的桥梁
  • 每个 HTML 标签在 DOM 中都是一个元素对象
  • DOM 以树形结构组织,有父子关系和兄弟关系
  • document 是 DOM 树的根节点,一切操作从这里开始
  • 元素节点是最常操作的节点类型
  • 文本内容保存在文本节点中,标签属性挂在元素节点上
  • nodeType 可以用来判断节点类型
  • firstChildlastChild 会包含换行、空格产生的文本节点
  • 只关心标签时,使用 firstElementChildlastElementChildchildren 更合适
  • JS 修改 DOM 后,浏览器会实时更新页面显示
  • 修改 DOM 不会改原始 HTML 文件,只改变内存中的结构和页面显示
  • 大部分 DOM 操作需要先获取元素,再对元素进行操作

注意事项

  • JS 操作 DOM 前,要确保目标元素已经被浏览器解析出来
  • 如果 JS 写在 <head> 里且没有 defer,执行时 <body> 可能还没加载
  • 推荐把 <script> 放在 <body> 结束前,或使用 defer 属性
  • 获取不到元素时,querySelector() 返回 null,继续操作会报错
  • 频繁操作 DOM 会影响页面性能,尤其是循环中反复修改 DOM 时

理解

可以把 DOM 理解为一棵”标签树”。

浏览器把 HTML 里的标签按嵌套关系搭成树形结构,每个标签变成一个”节点对象”。

JS 通过这些节点对象可以找到标签、读取内容、修改样式、添加新标签等。

我理解 DOM 的重点是:浏览器把 HTML 变成一棵对象树,JS 通过这棵树操作页面

document 就是这棵树的入口,拿到了它,就等于拿到了整个页面的操作权。

引出

理解了 DOM 之后,就可以学习 JS 获取元素,知道怎么找到页面上的具体元素。

后续可以结合 JS 修改元素JS 创建和删除元素JS 事件,实现完整的页面交互功能。

当页面内容会被其他代码动态增删时,怎样自动感知这些变化?可以了解 JS MutationObserver