Appearance
DOM 简介
DOM 的概念
DOM(Document Object Model,文档对象模型)是一种用于表示和操作 HTML、XML 文档的编程接口。它将文档结构化为一个树形结构,其中每个节点都是一个对象,代表文档中的一个部分(如元素、属性、文本等)。
DOM 的作用
DOM 允许 JavaScript 与网页内容进行交互,主要功能包括:
- 访问和修改元素:通过 DOM API 可以获取和修改网页中的元素
- 访问和修改属性:可以获取和修改元素的属性
- 访问和修改内容:可以获取和修改元素的文本内容
- 创建和删除元素:可以动态创建和删除元素
- 响应事件:可以为元素添加事件监听器,响应用户交互
DOM 树结构
DOM 将 HTML 文档表示为一个树形结构,称为 DOM 树。树的每个节点代表文档中的一个部分:
- 文档节点:根节点,代表整个文档
- 元素节点:代表 HTML 元素(如
<div>、<p>等) - 属性节点:代表元素的属性(如
id、class等) - 文本节点:代表元素中的文本内容
- 注释节点:代表注释
示例 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 的优缺点
优点
- 标准化:DOM 是 W3C 标准,被所有现代浏览器支持
- 功能强大:提供了丰富的 API 用于操作文档
- 跨平台:可以在不同的浏览器和平台上使用
- 动态交互:允许创建动态、交互式的网页
缺点
- 性能开销:DOM 操作可能会导致重排和重绘,影响性能
- API 复杂:DOM API 较为复杂,学习曲线较陡
- 浏览器兼容性:不同浏览器对 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 操作次数,提高代码性能和安全性。