DOM 入门教程(面向 T3/React 开发者精简版)
本教程砍掉了所有过时、不常用的DOM知识点,只讲你后续学React/Next/T3必须理解的核心内容,不用死记硬背API,理解原理即可,90%的原生DOM操作后续都会被React封装,你只需要搞懂底层逻辑。
一、DOM 到底是什么?
DOM 全称 Document Object Model(文档对象模型),是浏览器把HTML字符串解析后,存在内存里的可被JS操作的树形对象结构:
- 你写的HTML是静态字符串,浏览器读了之后会转成一个树形的JS对象,这个就是DOM树
- JS操作DOM之后,浏览器会自动把变化更新到页面上,这就是页面交互的底层原理
对应你之前问的根节点问题,DOM树的层级结构很清晰:
Document(整个文档的根对象,对应JS里的`document`)
└── <html> 元素(所有HTML标签的根节点,对应`document.documentElement`)
├── <head> 元素(对应`document.head`)
│ ├── <title>、<meta>、<link>等元信息标签
└── <body> 元素(对应`document.body`)
├── <div>、<h1>、<a>等业务内容标签
平时口头说的「根节点」默认指
<html>标签,React项目里的「业务根节点」一般是你挂在<body>里的<div id="root">容器。
二、必须了解的3种DOM节点类型
不用记所有节点类型,只需要知道最常用的3种:
- 元素节点:所有HTML标签(div、p、button等),是你操作最多的节点类型
- 文本节点:标签里的文字内容,比如
<p>你好</p>里的「你好」就是文本节点 - 属性节点:标签上的属性,比如
<a href="xxx">里的href就是属性节点
其他注释节点、文档片段节点等了解即可,几乎不会直接操作。
三、实际开发常用的DOM操作(了解原理即可,不用背)
1. 查找元素(最常用,哪怕写React偶尔也会用到)
现在主流只用这几个API,其他老旧的getElementsByTagName等不用记:
| API | 作用 | 示例 |
|---|---|---|
document.getElementById('id名') | 按id找唯一元素 | document.getElementById('root') |
document.querySelector('CSS选择器') | 按CSS选择器找第一个匹配的元素 | document.querySelector('.btn') |
document.querySelectorAll('CSS选择器') | 找所有匹配的元素,返回NodeList类数组 | document.querySelectorAll('li') |
注意:
querySelectorAll返回的不是真数组,要转数组用Array.from(document.querySelectorAll('li'))或者[...document.querySelectorAll('li')]。
2. 修改元素
修改内容
element.textContent = '新内容':替换元素内的纯文本,不会解析HTML标签,无XSS风险,推荐用element.innerHTML = '<span>新内容</span>':会解析HTML标签,有XSS注入风险,非必要不用
React默认所有渲染内容都会转义成纯文本,就是为了避免XSS风险,底层就是类似
textContent的逻辑。
修改属性
- 标准属性直接用
.操作:element.src = 'xxx.jpg'、element.className = 'active'(注意是className不是class,因为class是JS关键字) - 自定义属性用
setAttribute/getAttribute:element.setAttribute('data-id', '123')
修改样式
-
改类名(推荐,符合样式和逻辑分离原则):用
classListAPIconst btn = document.querySelector(".btn"); btn.classList.add("active"); // 加类 btn.classList.remove("active"); // 删类 btn.classList.toggle("active"); // 有就删没有就加,做切换场景非常方便 btn.classList.contains("active"); // 判断有没有某个类 -
直接改行内样式(少用):
element.style.color = 'red'
3. 增删节点(了解即可,React会帮你做)
// 新增节点
const div = document.createElement("div"); // 创建div元素
div.textContent = "我是新增的";
document.body.appendChild(div); // 把div加到body的最后
// 删除节点
div.remove(); // 直接删自己
// 或者父节点删子节点:document.body.removeChild(div)
四、核心重点:DOM 事件机制(必须彻底理解,原生/React都要用)
所有页面交互(点击、输入、滚动等)都是基于DOM事件实现的。
1. 事件绑定的正确方式
只用addEventListener即可,其他行内绑定、onclick属性绑定都不推荐:
const btn = document.querySelector(".btn");
// 第一个参数是事件名,第二个是触发事件的回调函数
btn.addEventListener("click", e => {
console.log("按钮被点击了");
});
2. 事件对象e的常用属性/方法
回调函数里的e就是事件对象,常用的只有这几个:
e.target:触发事件的真实元素e.currentTarget:绑定事件的元素(和target可能不一样,因为事件冒泡)e.stopPropagation():阻止事件冒泡e.preventDefault():阻止默认行为,比如阻止a标签跳转、阻止表单默认提交
3. 事件冒泡(必须理解)
事件触发后,会从触发的元素一层层往上传递到父元素,直到document根节点,这个就是冒泡。
举个例子:你点击了div里面的button,button的点击事件触发后,div的点击事件、body的点击事件也会依次触发。
如果不想让事件往上传,就在子元素的回调里加e.stopPropagation()。
React的合成事件就是基于事件冒泡原理实现的,所有事件都绑定在根节点上,性能更高,你懂了原生冒泡才能理解React合成事件的逻辑。
4. 事件委托(了解原理)
利用冒泡的特性,给父元素绑定事件,就能处理所有子元素的事件,不用给每个子元素单独绑,性能更高。比如你有1000个列表项要绑点击事件,不用循环给每个li绑,直接给父ul绑事件,通过e.target判断点的是哪个li即可。
五、学DOM对T3/React开发的意义
你写T3项目的时候90%的场景都不会直接操作原生DOM,但是你必须懂DOM原理:
- 理解React的运行逻辑:React的虚拟DOM本质就是对原生DOM的模拟,你改state之后React会比对新旧虚拟DOM的差异,然后调用原生DOM API更新真实DOM,懂了原生DOM你才知道React为什么这么设计。
- 特殊场景要用:比如你用
useRef获取真实DOM做滚动、聚焦、调用第三方依赖DOM的库的时候,你得知道拿到的DOM对象是什么、怎么操作。 - 排查问题:遇到事件冒泡冲突、样式异常、XSS风险等问题的时候,懂原生DOM才能快速定位根因。
六、小练习(10分钟就能做完,帮你巩固)
写一个静态HTML页面,实现:点击按钮切换文字的颜色,按钮文字在「开」「关」之间切换。
<!DOCTYPE html>
<html>
<body>
<p id="text">我是要变色的文字</p>
<button id="btn">开</button>
<script>
const text = document.getElementById("text");
const btn = document.getElementById("btn");
let isActive = false;
btn.addEventListener("click", () => {
isActive = !isActive;
text.style.color = isActive ? "red" : "black";
btn.textContent = isActive ? "关" : "开";
});
</script>
</body>
</html>
做完之后你可以想一下:如果用React写这个功能,你会怎么写?对比一下原生和框架的差异,就能直观感受到框架帮你省了多少手动操作DOM的代码。