JavaScript DOM 操作
DOM(文档对象模型)是 HTML 和 XML 文档的编程接口,它将文档表示为节点树,使 JavaScript 能够访问和操作文档的结构、样式和内容。
🎯 DOM 操作的重要性
DOM 操作是 JavaScript 与网页交互的核心,它允许我们动态修改网页内容、样式和结构,创建交互式的用户体验。掌握 DOM 操作是成为一名优秀前端开发者的关键。
什么是 DOM?
DOM 是一种树形结构,将 HTML 或 XML 文档的每个部分表示为一个节点:
- 文档节点:整个文档的根节点
- 元素节点:HTML 标签,如 <div>、<p> 等
- 属性节点:元素的属性,如 id、class、src 等
- 文本节点:元素内的文本内容
- 注释节点:HTML 注释
DOM 树结构
一个简单的 HTML 文档的 DOM 树结构如下:
Document
└── html
├── head
│ ├── title
│ │ └── "JavaScript DOM 教程"
│ └── meta
└── body
├── header
│ └── h1
│ └── "JavaScript DOM 操作"
├── nav
│ └── ul
│ ├── li
│ │ └── a
│ └── li
│ └── a
├── main
│ └── section
│ ├── h2
│ │ └── "什么是 DOM?"
│ └── p
│ └── "DOM 是..."
└── footer
└── p
└── "© 2026 JavaScript 教程"
DOM 选择元素的方法
1. getElementById() 方法
通过元素的 id 属性选择元素:
// HTML: <div id="myDiv">Hello</div>
// 使用 getElementById() 选择元素
const element = document.getElementById("myDiv");
console.log(element); // 输出: <div id="myDiv">Hello</div>
// 修改元素内容
element.textContent = "Hello, DOM!";
console.log(element.textContent); // 输出: Hello, DOM!
2. getElementsByClassName() 方法
通过元素的 class 属性选择元素集合:
// HTML: <div class="myClass">Div 1</div>
// <div class="myClass">Div 2</div>
// 使用 getElementsByClassName() 选择元素集合
const elements = document.getElementsByClassName("myClass");
console.log(elements); // 输出: HTMLCollection(2)
console.log(elements.length); // 输出: 2
// 遍历元素集合
for (let i = 0; i < elements.length; i++) {
console.log(elements[i].textContent);
}
// 输出:
// Div 1
// Div 2
3. getElementsByTagName() 方法
通过元素的标签名选择元素集合:
// HTML: <p>Paragraph 1</p>
// <p>Paragraph 2</p>
// <p>Paragraph 3</p>
// 使用 getElementsByTagName() 选择元素集合
const paragraphs = document.getElementsByTagName("p");
console.log(paragraphs); // 输出: HTMLCollection(3)
console.log(paragraphs.length); // 输出: 3
// 遍历元素集合
for (let i = 0; i < paragraphs.length; i++) {
console.log(paragraphs[i].textContent);
}
// 输出:
// Paragraph 1
// Paragraph 2
// Paragraph 3
4. querySelector() 方法
通过 CSS 选择器选择第一个匹配的元素:
// HTML: <div class="container">
// <p class="text">Hello</p>
// <p class="text">World</p>
// </div>
// 使用 querySelector() 选择元素
const firstParagraph = document.querySelector(".container .text");
console.log(firstParagraph); // 输出: <p class="text">Hello</p>
// 修改元素样式
firstParagraph.style.color = "red";
firstParagraph.style.fontSize = "20px";
5. querySelectorAll() 方法
通过 CSS 选择器选择所有匹配的元素:
// HTML: <div class="container">
// <p class="text">Hello</p>
// <p class="text">World</p>
// </div>
// 使用 querySelectorAll() 选择元素集合
const paragraphs = document.querySelectorAll(".container .text");
console.log(paragraphs); // 输出: NodeList(2)
console.log(paragraphs.length); // 输出: 2
// 遍历元素集合(使用 forEach)
paragraphs.forEach(function(paragraph) {
paragraph.style.color = "blue";
console.log(paragraph.textContent);
});
// 输出:
// Hello
// World
修改元素内容
1. textContent 属性
设置或获取元素的文本内容:
// HTML: <div id="myDiv">Hello</div>
const element = document.getElementById("myDiv");
// 获取文本内容
console.log(element.textContent); // 输出: Hello
// 设置文本内容
element.textContent = "Hello, JavaScript!";
console.log(element.textContent); // 输出: Hello, JavaScript!
2. innerHTML 属性
设置或获取元素的 HTML 内容:
// HTML: <div id="myDiv">Hello</div>
const element = document.getElementById("myDiv");
// 获取 HTML 内容
console.log(element.innerHTML); // 输出: Hello
// 设置 HTML 内容
element.innerHTML = "<strong>Hello</strong>, <em>JavaScript</em>!";
console.log(element.innerHTML); // 输出: <strong>Hello</strong>, <em>JavaScript</em>!
// 添加 HTML 内容
element.innerHTML += " <a href=\"#\">Learn more</a>";
3. innerText 属性
设置或获取元素的可见文本内容:
// HTML: <div id="myDiv"><span style="display: none;">Hidden</span> Hello</div>
const element = document.getElementById("myDiv");
// 获取可见文本内容
console.log(element.innerText); // 输出: Hello
// 设置文本内容
element.innerText = "Hello, World!";
console.log(element.innerText); // 输出: Hello, World!
修改元素属性
1. setAttribute() 方法
设置元素的属性:
// HTML: <img id="myImage" src="old.jpg" alt="Old Image">
const image = document.getElementById("myImage");
// 设置属性
image.setAttribute("src", "new.jpg");
image.setAttribute("alt", "New Image");
image.setAttribute("title", "This is a new image");
// 检查属性是否设置成功
console.log(image.src); // 输出: new.jpg
console.log(image.alt); // 输出: New Image
console.log(image.title); // 输出: This is a new image
2. getAttribute() 方法
获取元素的属性值:
// HTML: <a id="myLink" href="https://example.com" target="_blank">Example</a>
const link = document.getElementById("myLink");
// 获取属性值
const href = link.getAttribute("href");
const target = link.getAttribute("target");
console.log(href); // 输出: https://example.com
console.log(target); // 输出: _blank
3. removeAttribute() 方法
移除元素的属性:
// HTML: <input type="text" id="myInput" disabled>
const input = document.getElementById("myInput");
// 移除属性
input.removeAttribute("disabled");
// 检查属性是否移除成功
console.log(input.hasAttribute("disabled")); // 输出: false
4. 直接访问属性
对于某些常用属性,可以直接通过元素对象访问:
// HTML: <div id="myDiv" class="container">Hello</div>
const element = document.getElementById("myDiv");
// 直接访问和修改属性
element.id = "newId";
element.className = "newClass";
console.log(element.id); // 输出: newId
console.log(element.className); // 输出: newClass
// HTML: <a id="myLink" href="https://example.com">Example</a>
const link = document.getElementById("myLink");
link.href = "https://google.com";
link.textContent = "Google";
console.log(link.href); // 输出: https://google.com
console.log(link.textContent); // 输出: Google
修改元素样式
1. style 属性
直接修改元素的内联样式:
// HTML: <div id="myDiv">Hello</div>
const element = document.getElementById("myDiv");
// 修改单个样式属性
element.style.color = "red";
element.style.fontSize = "24px";
element.style.backgroundColor = "yellow";
element.style.padding = "10px";
element.style.margin = "5px";
// 注意:CSS 属性名中的连字符需要转换为驼峰命名法
element.style.borderRadius = "5px";
element.style.textAlign = "center";
// 获取样式属性值
console.log(element.style.color); // 输出: red
console.log(element.style.fontSize); // 输出: 24px
2. classList 属性
操作元素的 CSS 类:
// HTML: <div id="myDiv" class="container">Hello</div>
const element = document.getElementById("myDiv");
// 添加类
element.classList.add("active");
element.classList.add("highlight");
console.log(element.className); // 输出: container active highlight
// 移除类
element.classList.remove("active");
console.log(element.className); // 输出: container highlight
// 切换类(存在则移除,不存在则添加)
element.classList.toggle("active");
console.log(element.className); // 输出: container highlight active
element.classList.toggle("active");
console.log(element.className); // 输出: container highlight
// 检查类是否存在
console.log(element.classList.contains("highlight")); // 输出: true
console.log(element.classList.contains("active")); // 输出: false
创建和删除元素
1. createElement() 方法
创建新的元素节点:
// 创建新元素
const newDiv = document.createElement("div");
// 设置元素属性和内容
newDiv.id = "newDiv";
newDiv.className = "container";
newDiv.textContent = "Hello, New Element!";
// 添加样式
newDiv.style.color = "blue";
newDiv.style.fontSize = "18px";
// 将元素添加到文档中
const parentElement = document.getElementById("parent");
parentElement.appendChild(newDiv);
2. createTextNode() 方法
创建新的文本节点:
// 创建文本节点
const textNode = document.createTextNode("Hello, Text Node!");
// 将文本节点添加到元素中
const element = document.getElementById("myDiv");
element.appendChild(textNode);
3. appendChild() 方法
将元素添加到父元素的末尾:
// HTML: <ul id="myList">
// <li>Item 1</li>
// </ul>
const list = document.getElementById("myList");
// 创建新的列表项
const newItem = document.createElement("li");
newItem.textContent = "Item 2";
// 添加到列表末尾
list.appendChild(newItem);
// 创建另一个列表项
const anotherItem = document.createElement("li");
anotherItem.textContent = "Item 3";
// 添加到列表末尾
list.appendChild(anotherItem);
4. insertBefore() 方法
在指定元素之前插入新元素:
// HTML: <ul id="myList">
// <li id="item1">Item 1</li>
// <li id="item3">Item 3</li>
// </ul>
const list = document.getElementById("myList");
const item1 = document.getElementById("item1");
// 创建新的列表项
const newItem = document.createElement("li");
newItem.textContent = "Item 2";
// 在 item1 之后插入新元素(在 item3 之前)
list.insertBefore(newItem, item1.nextSibling);
5. removeChild() 方法
从父元素中移除子元素:
// HTML: <ul id="myList">
// <li id="item1">Item 1</li>
// <li id="item2">Item 2</li>
// <li id="item3">Item 3</li>
// </ul>
const list = document.getElementById("myList");
const item2 = document.getElementById("item2");
// 移除元素
list.removeChild(item2);
// 另一种方法:通过父元素移除自身
const item1 = document.getElementById("item1");
item1.parentNode.removeChild(item1);
6. remove() 方法
直接移除元素自身(ES6+):
// HTML: <div id="myDiv">Hello</div>
const element = document.getElementById("myDiv");
// 直接移除元素
element.remove();
DOM 遍历
1. 遍历子节点
// HTML: <div id="parent">
// <p>Paragraph 1</p>
// <p>Paragraph 2</p>
// <p>Paragraph 3</p>
// </div>
const parent = document.getElementById("parent");
// 获取子节点
const children = parent.children;
console.log(children); // 输出: HTMLCollection(3)
// 遍历子节点
for (let i = 0; i < children.length; i++) {
console.log(children[i].textContent);
}
// 输出:
// Paragraph 1
// Paragraph 2
// Paragraph 3
// 获取第一个和最后一个子元素
const firstChild = parent.firstElementChild;
const lastChild = parent.lastElementChild;
console.log(firstChild.textContent); // 输出: Paragraph 1
console.log(lastChild.textContent); // 输出: Paragraph 3
2. 遍历兄弟节点
// HTML: <div id="parent">
// <p id="p1">Paragraph 1</p>
// <p id="p2">Paragraph 2</p>
// <p id="p3">Paragraph 3</p>
// </div>
const p2 = document.getElementById("p2");
// 获取前一个和后一个兄弟元素
const previousSibling = p2.previousElementSibling;
const nextSibling = p2.nextElementSibling;
console.log(previousSibling.textContent); // 输出: Paragraph 1
console.log(nextSibling.textContent); // 输出: Paragraph 3
3. 遍历父节点
// HTML: <div id="grandparent">
// <div id="parent">
// <p id="child">Paragraph</p>
// </div>
// </div>
const child = document.getElementById("child");
// 获取父元素
const parent = child.parentElement;
const grandparent = parent.parentElement;
console.log(parent.id); // 输出: parent
console.log(grandparent.id); // 输出: grandparent
DOM 事件基础
DOM 事件允许我们响应用户的交互,如点击、鼠标移动、键盘输入等:
// HTML: <button id="myButton">Click Me</button>
const button = document.getElementById("myButton");
// 添加点击事件监听器
button.addEventListener("click", function() {
console.log("Button clicked!");
});
// 使用箭头函数
button.addEventListener("click", () => {
console.log("Button clicked with arrow function!");
});
// 传递事件对象
button.addEventListener("click", function(event) {
console.log("Event type:", event.type);
console.log("Target element:", event.target);
console.log("Client X:", event.clientX);
console.log("Client Y:", event.clientY);
});
// 移除事件监听器
function handleClick() {
console.log("Handled click!");
}
button.addEventListener("click", handleClick);
// 稍后移除
// button.removeEventListener("click", handleClick);
DOM 加载
确保 DOM 加载完成后再执行 JavaScript 代码:
// 方法 1: DOMContentLoaded 事件
document.addEventListener("DOMContentLoaded", function() {
// DOM 已经加载完成
const element = document.getElementById("myElement");
// 操作元素...
});
// 方法 2: window.onload 事件
window.onload = function() {
// 所有资源(包括图片、样式表等)都已加载完成
console.log("All resources loaded!");
};
// 方法 3: 将脚本放在 HTML 底部
// 在 HTML 文件的末尾,</body> 标签之前放置脚本
// <script src="script.js"></script>
// </body>
// </html>
DOM 操作最佳实践
- 使用现代选择器:优先使用 querySelector() 和 querySelectorAll(),它们更灵活,支持 CSS 选择器
- 缓存 DOM 元素:避免在循环中重复查询 DOM,将频繁使用的元素存储在变量中
- 批量操作 DOM:使用 DocumentFragment 或字符串拼接批量创建和添加元素,减少重排和重绘
- 使用事件委托:对于大量相似元素,使用事件委托提高性能
- 避免使用 innerHTML:innerHTML 可能导致 XSS 攻击,优先使用 textContent 或 createElement()
- 使用 CSS 类控制样式:优先通过添加/移除 CSS 类来修改样式,而不是直接操作 style 属性
- 处理 DOM 加载事件:确保在 DOM 加载完成后再执行操作
- 使用 requestAnimationFrame:对于动画效果,使用 requestAnimationFrame 提高性能
- 清理事件监听器:在元素移除前,移除其事件监听器,避免内存泄漏
- 使用现代 DOM API:了解并使用最新的 DOM API,如 classList、append()、prepend() 等
DOM 操作常见问题
- 问题 1: 元素未找到
- 问题 2: 事件监听器重复添加
- 问题 3: 内存泄漏
- 问题 4: 重排和重绘性能问题
- 问题 5: 跨浏览器兼容性
解决方案:确保元素存在于 DOM 中,并且在脚本执行前已经加载。使用 DOMContentLoaded 事件或将脚本放在 HTML 底部。
解决方案:避免在循环中重复添加事件监听器,使用事件委托或确保只添加一次监听器。
解决方案:移除不再需要的事件监听器,避免循环引用,特别是在单页应用中。
解决方案:批量操作 DOM,使用 DocumentFragment,避免频繁修改样式和布局属性。
解决方案:了解不同浏览器的 DOM API 差异,使用 polyfill 或现代框架处理兼容性问题。
📝 学习检查
通过本章节的学习,你应该了解:
- DOM 的基本概念和树结构
- 如何使用不同的方法选择 DOM 元素
- 如何修改元素的内容、属性和样式
- 如何创建、添加和删除 DOM 元素
- 如何遍历 DOM 树
- DOM 事件的基本概念
- DOM 操作的最佳实践和常见问题