JavaScript 教程

纯干货教学,从零开始学习 JavaScript

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: 元素未找到
  • 解决方案:确保元素存在于 DOM 中,并且在脚本执行前已经加载。使用 DOMContentLoaded 事件或将脚本放在 HTML 底部。

  • 问题 2: 事件监听器重复添加
  • 解决方案:避免在循环中重复添加事件监听器,使用事件委托或确保只添加一次监听器。

  • 问题 3: 内存泄漏
  • 解决方案:移除不再需要的事件监听器,避免循环引用,特别是在单页应用中。

  • 问题 4: 重排和重绘性能问题
  • 解决方案:批量操作 DOM,使用 DocumentFragment,避免频繁修改样式和布局属性。

  • 问题 5: 跨浏览器兼容性
  • 解决方案:了解不同浏览器的 DOM API 差异,使用 polyfill 或现代框架处理兼容性问题。

📝 学习检查

通过本章节的学习,你应该了解:

  • DOM 的基本概念和树结构
  • 如何使用不同的方法选择 DOM 元素
  • 如何修改元素的内容、属性和样式
  • 如何创建、添加和删除 DOM 元素
  • 如何遍历 DOM 树
  • DOM 事件的基本概念
  • DOM 操作的最佳实践和常见问题