JavaScript DOM 操作详解

当你使用 JavaScript 操作网页时,最核心的能力就是与 DOM(Document Object Model,文档对象模型) 进行交互。DOM 是浏览器将 HTML 文档解析后生成的一个树状结构,它将网页中的每一个元素、属性、文本都抽象为"节点"(Node)。有了 DOM,JavaScript 就可以动态地读取、修改、添加或删除页面上的任何内容。

一、什么是 DOM?

当浏览器加载一个 HTML 页面时,它会解析 HTML 代码,并构建一个 DOM 树。这个树形结构清晰地反映了 HTML 元素的嵌套关系。

例如,对于下面的 HTML 代码:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<title>我的页面</title>
</head>
<body>
<div id="main">
<h1>欢迎</h1>
<p class="intro">这是一段介绍文字。</p>
</div>
</body>
</html>

浏览器会生成如下的 DOM 树结构:

1
2
3
4
5
6
7
8
9
10
11
Document
└── html
├── head
│ └── title
│ └── "我的页面"
└── body
└── div#main
├── h1
│ └── "欢迎"
└── p.intro
└── "这是一段介绍文字。"

JavaScript 通过 DOM 提供的 API,可以轻松地遍历、查询和操作这棵树中的任意一个节点。

二、获取 DOM 元素

在对 DOM 元素进行操作之前,首先需要获取到它。JavaScript 提供了多种元素选择方法。

1. getElementById

通过元素的 id 属性获取,返回匹配的元素。

1
2
// HTML: <div id="header">...</div>
let element = document.getElementById('header');

2. getElementsByClassName

通过元素的 class 属性获取,返回一个类数组对象(HTMLCollection)。注意,即使只有一个元素,也要用索引来访问。

1
2
3
// HTML: <p class="intro">...</p>
let elements = document.getElementsByClassName('intro');
let firstElement = elements[0];

3. getElementsByTagName

通过元素标签名获取,返回一个类数组对象(HTMLCollection)。

1
2
3
// 获取所有 <p> 元素
let paragraphs = document.getElementsByTagName('p');
let firstParagraph = paragraphs[0];

4. querySelector

使用 CSS 选择器 获取匹配的第一个元素。这是最灵活、最推荐使用的选择方法。

1
2
3
4
5
6
7
8
// 通过 id 获取
let header = document.querySelector('#header');

// 通过 class 获取第一个
let intro = document.querySelector('.intro');

// 通过标签+类组合获取
let mainDiv = document.querySelector('div.main-content');

5. querySelectorAll

使用 CSS 选择器获取所有匹配的元素,返回一个 NodeList(节点列表)。NodeList 与 HTMLCollection 类似,但支持 forEach 等数组方法。

1
2
3
4
5
6
let items = document.querySelectorAll('.list-item');

// 遍历所有匹配元素
items.forEach(function(item) {
console.log(item.textContent);
});

6. 特殊的选择方法

1
2
3
4
5
6
7
8
// 获取根节点 <html>
let root = document.documentElement;

// 获取 <head> 节点
let head = document.head;

// 获取 <body> 节点
let body = document.body;

三、操作元素内容

1. innerHTML

获取或设置元素内部的 HTML 内容。它会解析字符串中的 HTML 标签。

1
2
3
4
5
6
7
let div = document.querySelector('#container');

// 获取内容
console.log(div.innerHTML);

// 设置内容(会解析 HTML 标签)
div.innerHTML = '<h2>新标题</h2><p>这是一段新内容。</p>';

2. textContent

获取或设置元素内部的纯文本内容,不会解析 HTML 标签。

1
2
3
4
5
6
7
let div = document.querySelector('#container');

// 获取纯文本
console.log(div.textContent);

// 设置纯文本
div.textContent = '这是一段纯文本内容。';

3. innerText

textContent 类似,但会考虑 CSS 样式(如隐藏元素的内容不会返回),性能略低于 textContent

四、操作元素属性

1. 标准属性的读写

1
2
3
4
5
6
7
8
9
let img = document.querySelector('#avatar');

// 读取属性
let src = img.getAttribute('src');
let alt = img.alt; // 直接用属性名也行

// 设置属性
img.setAttribute('src', 'new-image.jpg');
img.alt = '新的头像';

2. 操作 class 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
let element = document.querySelector('#box');

// 获取 class 字符串
console.log(element.className);

// 设置 class(会覆盖原有所有 class)
element.className = 'new-class';

// 使用 classList(推荐)
element.classList.add('active'); // 添加一个类
element.classList.remove('hidden'); // 移除一个类
element.classList.toggle('open'); // 切换类(有则移除,无则添加)
element.classList.contains('active'); // 检查是否包含某个类

3. 操作 style 属性(内联样式)

1
2
3
4
5
6
7
8
let element = document.querySelector('#box');

// 设置单个样式
element.style.color = 'red';
element.style.fontSize = '20px'; // 注意:CSS 中的 font-size 变为 fontSize

// 批量设置样式
element.style.cssText = 'color: blue; background-color: yellow;';

五、操作元素结构

1. 创建新元素

使用 createElement 创建新元素,使用 createTextNode 创建文本节点。

1
2
3
4
5
6
7
8
// 创建一个新的 <li> 元素
let newItem = document.createElement('li');

// 创建文本节点
let text = document.createTextNode('新项目');

// 将文本添加到元素中
newItem.appendChild(text);

2. 添加子元素

1
2
3
4
5
6
7
8
let list = document.querySelector('#myList');

// 在末尾添加子元素
list.appendChild(newItem);

// 在指定子元素之前插入
let firstItem = list.children[0];
list.insertBefore(newItem, firstItem);

3. 删除子元素

1
2
3
4
5
6
7
8
let list = document.querySelector('#myList');
let firstItem = list.children[0];

// 删除第一个子元素
list.removeChild(firstItem);

// 或者使用更现代的 remove() 方法
firstItem.remove();

4. 替换子元素

1
2
3
4
5
6
7
let list = document.querySelector('#myList');
let oldItem = list.children[0];
let newItem = document.createElement('li');
newItem.textContent = '替换后的内容';

// 用新元素替换旧元素
list.replaceChild(newItem, oldItem);

5. 克隆元素

1
2
3
let original = document.querySelector('#card');
let clone = original.cloneNode(true); // true 表示深度克隆(包含所有子元素)
let cloneShallow = original.cloneNode(false); // false 表示浅克隆(只克隆元素本身)

六、遍历 DOM 树

1. 父子关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let element = document.querySelector('#main');

// 父节点
let parent = element.parentNode;

// 子节点列表(包含文本节点)
let children = element.childNodes;

// 子元素列表(只包含 HTML 元素节点)
let childElements = element.children;

// 第一个子节点
let firstChild = element.firstChild;

// 第一个子元素
let firstElementChild = element.firstElementChild;

// 最后一个子节点
let lastChild = element.lastChild;

// 最后一个子元素
let lastElementChild = element.lastElementChild;

2. 兄弟关系

1
2
3
4
5
6
7
8
9
10
11
12
13
let element = document.querySelector('#target');

// 前一个兄弟节点
let prevSibling = element.previousSibling;

// 前一个兄弟元素
let prevElementSibling = element.previousElementSibling;

// 下一个兄弟节点
let nextSibling = element.nextSibling;

// 下一个兄弟元素
let nextElementSibling = element.nextElementSibling;

七、事件处理

事件是 JavaScript 与用户交互的核心机制。

1. addEventListener 方法(推荐)

1
2
3
4
5
6
7
8
9
10
let button = document.querySelector('#myButton');

button.addEventListener('click', function() {
alert('按钮被点击了!');
});

// 可以添加多个事件监听器
button.addEventListener('mouseenter', function() {
console.log('鼠标进入了按钮区域');
});

2. 常见事件类型

鼠标事件clickdblclickmousedownmouseupmouseentermouseleavemousemove

键盘事件keydownkeyupkeypress(已不推荐使用)

表单事件submitchangeinputfocusblur

窗口事件loadresizescrollbeforeunload

3. 事件对象

事件处理函数会自动接收一个 event 参数,包含事件的详细信息。

1
2
3
4
5
6
7
document.querySelector('#myInput').addEventListener('keydown', function(event) {
console.log('按下的键:' + event.key);
console.log('键码:' + event.keyCode);

// 阻止默认行为(比如阻止表单提交)
event.preventDefault();
});

4. 事件冒泡与事件委托

事件冒泡是指当一个元素上的事件被触发时,这个事件会逐层向上传播到它的父元素。利用这个机制,可以实现事件委托

1
2
3
4
5
6
7
8
9
10
11
// 为整个列表添加一个点击事件监听器
let list = document.querySelector('#myList');

list.addEventListener('click', function(event) {
// event.target 是被点击的具体元素
if (event.target.tagName === 'LI') {
console.log('点击了列表项:' + event.target.textContent);
}
});

// 这样就不需要为每个 <li> 单独绑定事件,即使动态添加的 <li> 也能响应点击

八、表单操作

1. 获取表单元素的值

1
2
3
4
5
let input = document.querySelector('#username');
let value = input.value; // 获取输入框的值

let checkbox = document.querySelector('#agree');
let isChecked = checkbox.checked; // 获取复选框的选中状态

2. 表单验证与提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let form = document.querySelector('#loginForm');

form.addEventListener('submit', function(event) {
// 阻止表单默认提交行为
event.preventDefault();

let username = this.querySelector('#username').value;
let password = this.querySelector('#password').value;

if (username === '' || password === '') {
alert('请填写完整信息');
return;
}

// 此处可进行 AJAX 提交或其他逻辑
console.log('提交数据:', { username, password });
});

九、元素位置与尺寸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let element = document.querySelector('#box');

// 获取元素相对于视口的位置
let rect = element.getBoundingClientRect();
console.log(rect.top, rect.left, rect.width, rect.height);

// 获取元素的滚动位置
let scrollTop = element.scrollTop;
let scrollLeft = element.scrollLeft;

// 获取元素内部尺寸(不含边框)
let clientWidth = element.clientWidth;
let clientHeight = element.clientHeight;

// 获取元素完整尺寸(含边框和内边距)
let offsetWidth = element.offsetWidth;
let offsetHeight = element.offsetHeight;

总结

DOM 操作是 JavaScript 与网页交互的核心能力。掌握如何获取元素、修改内容与样式、动态调整结构、处理用户事件,是构建交互式网页应用的基础。通过 DOM API,你可以让网页从静态的 HTML 文档,转变为动态的、可交互的 Web 应用。熟练运用选择器、事件机制和 DOM 遍历方法,将使你能够高效地实现各种前端交互功能。