JavaScript DOM 操作
DOM(Document Object Model,文档对象模型)是 JavaScript 操作网页的接口。
DOM 基础
什么是 DOM
DOM 将 HTML 文档解析为一个树形结构,每个 HTML 元素都是树上的一个节点。
document
├── html
│ ├── head
│ │ ├── title
│ │ └── meta
│ └── body
│ ├── header
│ ├── main
│ └── footer
DOM 树结构
- 文档节点(Document):整个文档的入口
- 元素节点(Element):HTML 标签
- 文本节点(Text):标签内的文本
- 属性节点(Attr):标签的属性
- 注释节点(Comment):HTML 注释
获取元素
getElementById
通过 ID 获取单个元素:
const header = document.getElementById("header");
console.log(header);
getElementsByClassName
通过类名获取元素集合:
const buttons = document.getElementsByClassName("btn");
// 返回 HTMLCollection,需要用索引访问
console.log(buttons[0]);
getElementsByTagName
通过标签名获取元素集合:
const paragraphs = document.getElementsByTagName("p");
console.log(paragraphs.length);
querySelector
通过 CSS 选择器获取单个元素:
// 获取第一个匹配的元素
const nav = document.querySelector(".nav");
const submitBtn = document.querySelector("#submit-btn");
const firstListItem = document.querySelector("ul li");
querySelectorAll
通过 CSS 选择器获取所有匹配的元素:
const allLinks = document.querySelectorAll("a");
const cardItems = document.querySelectorAll(".card .item");
// 使用 forEach 遍历
allLinks.forEach(link => {
console.log(link.href);
});
DOM 元素操作
获取和设置内容
const element = document.querySelector("#title");
// 获取内容
console.log(element.textContent); // 获取所有文本
console.log(element.innerHTML); // 获取 HTML 内容
// 设置内容
element.textContent = "新标题";
element.innerHTML = "<span>新的</span> 标题";
// 区别:
// textContent 会转义 HTML 标签
// innerHTML 会解析 HTML 标签
获取和设置属性
const link = document.querySelector("a");
// 获取属性
console.log(link.getAttribute("href"));
console.log(link.href); // 完整 URL
// 设置属性
link.setAttribute("href", "https://example.com");
link.href = "https://example.com";
// 检查属性
console.log(link.hasAttribute("target")); // true/false
// 移除属性
link.removeAttribute("target");
// 设置布尔属性
input.disabled = true;
input.setAttribute("disabled", "");
class 操作
const box = document.querySelector(".box");
// 添加类
box.classList.add("active");
box.classList.add("highlight", "rounded");
// 移除类
box.classList.remove("active");
box.classList.remove("highlight", "rounded");
// 切换类(存在则移除,不存在则添加)
box.classList.toggle("active");
// 检查是否包含某个类
console.log(box.classList.contains("active")); // true/false
// 替换类
box.classList.replace("old-class", "new-class");
style 操作
const box = document.querySelector(".box");
// 设置单个样式
box.style.backgroundColor = "blue";
box.style.fontSize = "16px";
// 设置多个样式
Object.assign(box.style, {
color: "white",
padding: "20px",
borderRadius: "5px"
});
// 获取计算后的样式
const computedStyle = window.getComputedStyle(box);
console.log(computedStyle.backgroundColor);
data-* 属性
const card = document.querySelector(".card");
// 设置 data 属性
card.dataset.userId = "123";
card.dataset.role = "admin";
// 获取 data 属性
console.log(card.dataset.userId); // "123"
console.log(card.dataset.role); // "admin"
// 移除 data 属性
delete card.dataset.userId;
DOM 元素关系
父节点和子节点
const list = document.querySelector("ul");
// 父节点
const parent = list.parentNode;
const parentElement = list.parentElement;
// 子节点
const children = list.childNodes; // 包括文本节点
const childElements = list.children; // 只有元素节点
const firstChild = list.firstChild;
const lastChild = list.lastChild;
const firstElement = list.firstElementChild;
const lastElement = list.lastElementChild;
兄弟节点
const item = document.querySelector(".item");
// 前一个兄弟
const prev = item.previousSibling; // 包括文本节点
const prevElement = item.previousElementSibling;
// 后一个兄弟
const next = item.nextSibling; // 包括文本节点
const nextElement = item.nextElementSibling;
创建和删除元素
创建元素
// 创建新元素
const newDiv = document.createElement("div");
newDiv.textContent = "新创建的 div";
newDiv.className = "new-div";
newDiv.id = "unique-id";
// 创建文本节点
const textNode = document.createTextNode("这是文本");
// 创建文档片段(一次性添加到 DOM)
const fragment = document.createDocumentFragment();
for (let i = 0; i < 5; i++) {
const li = document.createElement("li");
li.textContent = `列表项 ${i + 1}`;
fragment.appendChild(li);
}
document.querySelector("ul").appendChild(fragment);
添加元素
const container = document.querySelector(".container");
const newElement = document.createElement("div");
// appendChild - 添加到末尾
container.appendChild(newElement);
// prepend - 添加到开头
container.prepend(newElement);
// before - 插入到元素之前
existingElement.before(newElement);
// after - 插入到元素之后
existingElement.after(newElement);
// insertAdjacentHTML
container.insertAdjacentHTML("beforeend", "<div>新元素</div>");
// beforebegin: 在元素前插入
// afterbegin: 在元素内开头插入
// beforeend: 在元素内末尾插入
// afterend: 在元素后插入
删除元素
const element = document.querySelector(".to-remove");
// 删除元素
element.remove();
// 或者通过父节点删除
element.parentNode.removeChild(element);
替换元素
const oldElement = document.querySelector(".old");
const newElement = document.createElement("div");
newElement.textContent = "新元素";
oldElement.replaceWith(newElement);
// 或者
oldElement.parentNode.replaceChild(newElement, oldElement);
元素尺寸和位置
offset 系列
const box = document.querySelector(".box");
console.log(box.offsetParent); // 定位祖先元素
console.log(box.offsetTop); // 距定位祖先的顶部距离
console.log(box.offsetLeft); // 距定位祖先的左侧距离
console.log(box.offsetWidth); // width + padding + border
console.log(box.offsetHeight); // height + padding + border
client 系列
console.log(box.clientTop); // border 宽度
console.log(box.clientLeft); // border 宽度
console.log(box.clientWidth); // width + padding(不含 border)
console.log(box.clientHeight); // height + padding(不含 border)
scroll 系列
console.log(box.scrollTop); // 已滚动上去的像素
console.log(box.scrollLeft); // 已滚动左边的像素
console.log(box.scrollWidth); // 内容的总宽度
console.log(box.scrollHeight); // 内容的总高度
getBoundingClientRect
const rect = box.getBoundingClientRect();
console.log(rect.x); // 元素左边相对于视口的坐标
console.log(rect.y); // 元素顶部相对于视口的坐标
console.log(rect.width); // 元素宽度(包含 border)
console.log(rect.height); // 元素高度(包含 border)
console.log(rect.top); // 元素上边距视口顶部的距离
console.log(rect.bottom); // 元素下边距视口顶部的距离
console.log(rect.left); // 元素左边距视口左边的距离
console.log(rect.right); // 元素右边距视口左边的距离
实战案例
留言板功能
const messageInput = document.querySelector("#message-input");
const submitBtn = document.querySelector("#submit-btn");
const messageList = document.querySelector("#message-list");
submitBtn.addEventListener("click", () => {
const message = messageInput.value.trim();
if (!message) {
alert("请输入留言内容");
return;
}
const messageItem = document.createElement("div");
messageItem.className = "message-item";
messageItem.innerHTML = `
<p class="message-text">${escapeHtml(message)}</p>
<button class="delete-btn">删除</button>
`;
messageList.appendChild(messageItem);
messageInput.value = "";
messageItem.querySelector(".delete-btn").addEventListener("click", () => {
messageItem.remove();
});
});
function escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
回到顶部
const backToTopBtn = document.querySelector("#back-to-top");
window.addEventListener("scroll", () => {
if (window.scrollY > 300) {
backToTopBtn.style.display = "block";
} else {
backToTopBtn.style.display = "none";
}
});
backToTopBtn.addEventListener("click", () => {
window.scrollTo({
top: 0,
behavior: "smooth"
});
});
小结
- DOM 将 HTML 文档解析为树形结构
- 通过选择器(getElementById、querySelector 等)获取元素
- 操作元素的内容、属性、样式
- 创建、添加、删除 DOM 元素
- 获取元素的尺寸和位置信息
练习
- 实现一个待办事项列表(添加、完成、删除)
- 实现图片懒加载功能
- 实现一个简单的轮播图组件
- 实现表格的全选、反选功能