Skip to content

DOM 简介

DOM 的概念

DOM(Document Object Model,文档对象模型)是一种用于表示和操作 HTML、XML 文档的编程接口。它将文档结构化为一个树形结构,其中每个节点都是一个对象,代表文档中的一个部分(如元素、属性、文本等)。

DOM 的作用

DOM 允许 JavaScript 与网页内容进行交互,主要功能包括:

  1. 访问和修改元素:通过 DOM API 可以获取和修改网页中的元素
  2. 访问和修改属性:可以获取和修改元素的属性
  3. 访问和修改内容:可以获取和修改元素的文本内容
  4. 创建和删除元素:可以动态创建和删除元素
  5. 响应事件:可以为元素添加事件监听器,响应用户交互

DOM 树结构

DOM 将 HTML 文档表示为一个树形结构,称为 DOM 树。树的每个节点代表文档中的一个部分:

  • 文档节点:根节点,代表整个文档
  • 元素节点:代表 HTML 元素(如 <div><p> 等)
  • 属性节点:代表元素的属性(如 idclass 等)
  • 文本节点:代表元素中的文本内容
  • 注释节点:代表注释

示例 DOM 树

html
<!DOCTYPE html>
<html>
<head>
  <title>DOM 示例</title>
</head>
<body>
  <div id="container">
    <h1>Hello, DOM!</h1>
    <p>This is a paragraph.</p>
  </div>
</body>
</html>

对应的 DOM 树结构:

document
└── html
    ├── head
    │   └── title
    │       └── "DOM 示例"
    └── body
        └── div (id="container")
            ├── h1
            │   └── "Hello, DOM!"
            └── p
                └── "This is a paragraph."

DOM 节点类型

DOM 中的节点有不同的类型,每种类型都有特定的属性和方法:

节点类型常量描述
文档节点Node.DOCUMENT_NODE (9)代表整个文档
元素节点Node.ELEMENT_NODE (1)代表 HTML 元素
属性节点Node.ATTRIBUTE_NODE (2)代表元素的属性
文本节点Node.TEXT_NODE (3)代表文本内容
注释节点Node.COMMENT_NODE (8)代表注释
javascript
// 示例:获取节点类型
const container = document.getElementById('container');
console.log(container.nodeType); // 输出: 1(元素节点)

const textNode = container.firstChild;
console.log(textNode.nodeType); // 输出: 3(文本节点,可能是空白字符)

DOM API

DOM 提供了一系列 API 用于操作文档:

1. 文档对象 (document)

document 对象是 DOM 的入口点,代表整个文档:

javascript
// 获取文档标题
document.title = '新标题';
console.log(document.title); // 输出: 新标题

// 获取文档 URL
console.log(document.URL); // 输出: 当前页面 URL

// 获取文档域名
console.log(document.domain); // 输出: 当前页面域名

2. 元素选择 API

用于获取文档中的元素:

  • getElementById():通过 ID 获取元素
  • getElementsByClassName():通过类名获取元素集合
  • getElementsByTagName():通过标签名获取元素集合
  • querySelector():通过 CSS 选择器获取第一个匹配的元素
  • querySelectorAll():通过 CSS 选择器获取所有匹配的元素
javascript
// 通过 ID 获取元素
const container = document.getElementById('container');

// 通过类名获取元素集合
const items = document.getElementsByClassName('item');

// 通过标签名获取元素集合
const paragraphs = document.getElementsByTagName('p');

// 通过 CSS 选择器获取第一个匹配的元素
const firstItem = document.querySelector('.item');

// 通过 CSS 选择器获取所有匹配的元素
const allItems = document.querySelectorAll('.item');

3. 元素操作 API

用于操作元素的属性和内容:

  • innerHTML:获取或设置元素的 HTML 内容
  • textContent:获取或设置元素的文本内容
  • setAttribute():设置元素的属性
  • getAttribute():获取元素的属性
  • removeAttribute():移除元素的属性
javascript
const container = document.getElementById('container');

// 设置 HTML 内容
container.innerHTML = '<h1>Hello, DOM!</h1><p>This is a paragraph.</p>';

// 获取文本内容
console.log(container.textContent); // 输出: Hello, DOM! This is a paragraph.

// 设置属性
container.setAttribute('class', 'container');

// 获取属性
console.log(container.getAttribute('class')); // 输出: container

// 移除属性
container.removeAttribute('class');

4. 节点操作 API

用于创建、添加、删除和替换节点:

  • createElement():创建新元素
  • createTextNode():创建新文本节点
  • appendChild():向元素添加子节点
  • removeChild():从元素中移除子节点
  • replaceChild():替换元素中的子节点
  • insertBefore():在指定节点前插入新节点
javascript
// 创建新元素
const newDiv = document.createElement('div');
newDiv.textContent = 'New div';

// 创建新文本节点
const newText = document.createTextNode('New text');

// 添加子节点
const container = document.getElementById('container');
container.appendChild(newDiv);

// 移除子节点
container.removeChild(newDiv);

// 替换子节点
const oldP = document.querySelector('p');
const newP = document.createElement('p');
newP.textContent = 'New paragraph';
container.replaceChild(newP, oldP);

// 插入节点
const h1 = document.querySelector('h1');
container.insertBefore(newDiv, h1);

DOM 事件

DOM 事件是用户与网页交互时触发的动作,如点击、鼠标移动、键盘输入等:

javascript
const button = document.getElementById('btn');

// 添加事件监听器
button.addEventListener('click', function() {
  console.log('Button clicked!');
});

// 移除事件监听器
function handleClick() {
  console.log('Button clicked!');
}

button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick);

DOM 的优缺点

优点

  1. 标准化:DOM 是 W3C 标准,被所有现代浏览器支持
  2. 功能强大:提供了丰富的 API 用于操作文档
  3. 跨平台:可以在不同的浏览器和平台上使用
  4. 动态交互:允许创建动态、交互式的网页

缺点

  1. 性能开销:DOM 操作可能会导致重排和重绘,影响性能
  2. API 复杂:DOM API 较为复杂,学习曲线较陡
  3. 浏览器兼容性:不同浏览器对 DOM API 的实现可能存在差异

DOM 操作的最佳实践

1. 减少 DOM 操作次数

DOM 操作是昂贵的,应该尽量减少操作次数:

javascript
// 不好的做法:多次 DOM 操作
const container = document.getElementById('container');
for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div');
  div.textContent = `Item ${i}`;
  container.appendChild(div);
}

// 好的做法:使用文档片段减少 DOM 操作
const container = document.getElementById('container');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div');
  div.textContent = `Item ${i}`;
  fragment.appendChild(div);
}
container.appendChild(fragment);

2. 使用现代选择器 API

优先使用 querySelector()querySelectorAll(),它们更灵活:

javascript
// 好的做法:使用 querySelector()
const element = document.querySelector('#container .item');

// 好的做法:使用 querySelectorAll()
const elements = document.querySelectorAll('.item');

3. 缓存 DOM 引用

避免重复获取相同的 DOM 元素:

javascript
// 不好的做法:重复获取 DOM 元素
for (let i = 0; i < 10; i++) {
  document.getElementById('counter').textContent = i;
}

// 好的做法:缓存 DOM 引用
const counter = document.getElementById('counter');
for (let i = 0; i < 10; i++) {
  counter.textContent = i;
}

4. 使用事件委托

对于多个相似元素的事件,使用事件委托可以提高性能:

javascript
// 不好的做法:为每个元素添加事件监听器
const items = document.querySelectorAll('.item');
items.forEach(item => {
  item.addEventListener('click', function() {
    console.log('Item clicked:', this.textContent);
  });
});

// 好的做法:使用事件委托
const container = document.getElementById('container');
container.addEventListener('click', function(event) {
  if (event.target.classList.contains('item')) {
    console.log('Item clicked:', event.target.textContent);
  }
});

5. 避免使用 innerHTML

innerHTML 可能会导致安全问题(如 XSS 攻击),并且性能较差:

javascript
// 不好的做法:使用 innerHTML
const container = document.getElementById('container');
container.innerHTML = '<div>New content</div>';

// 好的做法:使用 DOM 方法
const container = document.getElementById('container');
const newDiv = document.createElement('div');
newDiv.textContent = 'New content';
container.appendChild(newDiv);

小结

DOM 是 JavaScript 与网页内容交互的桥梁,它将 HTML 文档表示为一个树形结构,提供了丰富的 API 用于操作文档。理解 DOM 的基本概念和操作方法,是学习 JavaScript 前端开发的重要基础。在实际开发中,应该遵循最佳实践,减少 DOM 操作次数,提高代码性能和安全性。