前端三基石(三) JavaScript DOM 操作与事件处理
目录
- 前言
- 一、DOM 是什么?
- 二、获取元素
- 2.1 传统方法
- 2.2 选择器方法(推荐)
- 三、操作元素内容
- 3.1 innerHTML
- 3.2 textContent vs innerText
- 四、操作元素属性
- 4.1 标准属性
- 4.2 自定义属性
- 五、操作元素样式
- 5.1 style 属性
- 5.2 classList(推荐)
- 六、动态创建与删除元素
- 6.1 创建元素
- 6.2 添加元素
- 6.3 删除元素
- 七、事件处理
- 7.1 事件绑定方式
- 7.2 常用事件
- 7.3 事件对象
- 7.4 事件委托
- 八、总结
前言
在上一篇中,我们掌握了 JavaScript 的核心语法——变量、函数、对象、运算符等。但学会语法只是第一步,JavaScript 在浏览器中的真正威力在于:操作网页元素、响应用户交互。
这需要理解 DOM(Document Object Model)——浏览器提供给 JS 操作网页的接口。
本文是"前端三基石"系列第二篇的续篇,聚焦 DOM 操作与事件处理。
一、DOM 是什么?
DOM 是浏览器将 HTML 解析成的一棵节点树:
<html><head><title>示例</title></head><body><divid="container"><pclass="text">Hello</p></div></body></html>浏览器会将其解析为:
Document └── html ├── head │ └── title └── body └── div#container └── p.text └── "Hello"每个 HTML 标签都是一个元素节点,文本内容是文本节点。JS 通过document对象访问这棵树。
后端类比:DOM 类似于 Java 解析 XML 的 DOM 模型——把文档结构化为对象树,通过 API 增删改查节点。
二、获取元素
操作元素的第一步是"找到它"。
2.1 传统方法
// 通过 id 获取(唯一)document.getElementById('container');// 通过标签名获取(返回集合)document.getElementsByTagName('div');// 通过类名获取(返回集合)document.getElementsByClassName('text');这些方法返回的是HTMLCollection,是"活的"集合——文档变化时自动更新。
2.2 选择器方法(推荐)
ES5 提供了更灵活的选择器方法:
// 获取单个元素(第一个匹配的)document.querySelector('#container');document.querySelector('.text');document.querySelector('div p');// 获取所有匹配的元素(返回 NodeList)document.querySelectorAll('.text');document.querySelectorAll('div p');| 方法 | 返回值 | 特点 |
|---|---|---|
querySelector() | 单个元素 | 找不到返回null |
querySelectorAll() | NodeList | 找不到返回空列表(不是null) |
推荐使用选择器方法:语法统一(与 CSS 选择器一致),代码更清晰。
三、操作元素内容
3.1 innerHTML
读取或设置元素的 HTML 内容:
<divid="box"><span>Hello</span></div><script>constbox=document.getElementById('box');// 读取(得到 HTML 字符串)console.log(box.innerHTML);// "\n <span>Hello</span>\n"// 设置(会解析 HTML)box.innerHTML='<strong>World</strong>';</script>安全警告:不要将用户输入直接赋值给innerHTML,会导致 XSS 攻击:
// 危险!用户输入可能包含恶意脚本box.innerHTML=userInput;// ❌// 安全的方式:使用 textContentbox.textContent=userInput;// ✅3.2 textContent vs innerText
两者都操作纯文本,但有区别:
<divid="box"style="display:none">Hello<span>World</span></div><script>constbox=document.getElementById('box');console.log(box.textContent);// "Hello World"(获取所有文本)console.log(box.innerText);// ""(考虑 CSS 样式,隐藏元素返回空)</script>| 属性 | 特点 | 性能 |
|---|---|---|
textContent | 获取所有文本,不考虑 CSS | 更快 |
innerText | 考虑 CSS 样式(如display: none) | 较慢 |
推荐使用
textContent,除非你需要考虑 CSS 样式。
四、操作元素属性
4.1 标准属性
对于 HTML 标准属性(如id、className、href、src),可以直接访问:
constlink=document.querySelector('a');// 读取console.log(link.href);console.log(link.id);// 设置link.href='https://example.com';link.id='newId';// 特殊:class 要用 className(因为 class 是 JS 保留字)element.className='active';4.2 自定义属性
HTML5 允许使用data-*自定义属性:
<divid="box"data-user-id="123"data-role="admin"></div><script>constbox=document.getElementById('box');// 读取console.log(box.dataset.userId);// "123"(自动转为驼峰)console.log(box.dataset.role);// "admin"// 设置box.dataset.userId='456';</script>也可以使用通用方法:
// 任意属性都可用element.getAttribute('data-user-id');element.setAttribute('data-user-id','789');element.removeAttribute('data-user-id');五、操作元素样式
5.1 style 属性
直接修改内联样式:
constbox=document.getElementById('box');box.style.width='100px';box.style.height='50px';box.style.backgroundColor='red';// 注意:用驼峰命名box.style.display='none';缺点:
- 只能设置内联样式,无法操作 CSS 类
- 样式名需要转为驼峰(
background-color→backgroundColor)
5.2 classList(推荐)
通过操作 CSS 类来控制样式:
<style>.active{background-color:red;}.hidden{display:none;}</style><divid="box">Hello</div><script>constbox=document.getElementById('box');// 添加类box.classList.add('active');// 移除类box.classList.remove('active');// 切换类(有则移除,无则添加)box.classList.toggle('hidden');// 判断是否包含类if(box.classList.contains('active')){console.log('box 是激活状态');}</script>| 方法 | 作用 |
|---|---|
add() | 添加类 |
remove() | 移除类 |
toggle() | 切换类 |
contains() | 判断是否包含 |
replace(old, new) | 替换类 |
推荐使用
classList:样式与行为分离,易于维护。
六、动态创建与删除元素
6.1 创建元素
// 创建元素constdiv=document.createElement('div');// 设置属性和内容div.id='newDiv';div.className='box';div.textContent='新创建的元素';// 或使用 innerHTMLdiv.innerHTML='<span>内容</span>';6.2 添加元素
<divid="container"><pid="first">第一段</p></div><script>constcontainer=document.getElementById('container');constnewDiv=document.createElement('div');newDiv.textContent='新元素';// 追加到末尾container.appendChild(newDiv);// 插入到指定元素之前constfirst=document.getElementById('first');container.insertBefore(newDiv,first);// 新方法(更直观)container.append(newDiv);// 末尾添加container.prepend(newDiv);// 开头添加first.before(newDiv);// 在 first 之前插入first.after(newDiv);// 在 first 之后插入</script>6.3 删除元素
constbox=document.getElementById('box');// 传统方法box.parentNode.removeChild(box);// 新方法(推荐)box.remove();七、事件处理
7.1 事件绑定方式
方式一:HTML 属性(不推荐)
<buttononclick="alert('点击了')">点击</button>缺点:HTML 与 JS 混在一起,难以维护。
方式二:DOM 属性
constbtn=document.getElementById('btn');btn.onclick=function(){alert('点击了');};// 缺点:只能绑定一个处理函数,后绑定的会覆盖前面的方式三:addEventListener(推荐)
constbtn=document.getElementById('btn');btn.addEventListener('click',function(){alert('点击了');});// 优点:可以绑定多个处理函数btn.addEventListener('click',function(){console.log('另一个处理函数');});// 移除事件(需要保持同一个函数引用)functionhandleClick(){console.log('点击');}btn.addEventListener('click',handleClick);btn.removeEventListener('click',handleClick);7.2 常用事件
| 事件 | 触发时机 |
|---|---|
click | 点击 |
dblclick | 双击 |
mouseenter | 鼠标进入 |
mouseleave | 鼠标离开 |
keydown | 键盘按下 |
keyup | 键盘抬起 |
focus | 获得焦点 |
blur | 失去焦点 |
input | 输入内容变化(实时) |
change | 值改变并失焦 |
submit | 表单提交 |
load | 资源加载完成 |
DOMContentLoaded | DOM 解析完成 |
示例:
// 输入框实时响应constinput=document.querySelector('input');input.addEventListener('input',function(){console.log('当前值:',this.value);});// 表单提交constform=document.querySelector('form');form.addEventListener('submit',function(e){e.preventDefault();// 阻止默认提交行为console.log('表单数据:',newFormData(this));});7.3 事件对象
事件处理函数会收到一个事件对象:
btn.addEventListener('click',function(e){console.log(e.target);// 触发事件的元素(实际点击的)console.log(e.currentTarget);// 绑定事件的元素(this 指向它)console.log(e.type);// 事件类型console.log(e.clientX,e.clientY);// 鼠标坐标e.preventDefault();// 阻止默认行为e.stopPropagation();// 阻止冒泡});target vs currentTarget:
<ulid="list"><li>项目1</li><li>项目2</li></ul><script>constlist=document.getElementById('list');list.addEventListener('click',function(e){console.log(e.target);// 实际点击的 liconsole.log(e.currentTarget);// ul});</script>7.4 事件委托
问题:动态添加的元素没有事件绑定
// 只对已有的 li 生效,新添加的没有事件document.querySelectorAll('li').forEach(li=>{li.addEventListener('click',function(){console.log(this.textContent);});});解决:事件委托——将事件绑定到父元素,利用冒泡机制
constlist=document.getElementById('list');list.addEventListener('click',function(e){// 判断点击的是否是 liif(e.target.tagName==='LI'){console.log(e.target.textContent);}});优点:
- 动态元素也有事件
- 代码更简洁,内存占用更少
八、总结
| 类别 | 内容 | 推荐方法 |
|---|---|---|
| 获取元素 | 选择元素 | querySelector/querySelectorAll |
| 操作内容 | 读写文本/HTML | textContent/innerHTML(注意安全) |
| 操作属性 | 标准属性/自定义属性 | 直接访问 /dataset |
| 操作样式 | 控制样式 | classList |
| 创建元素 | 动态生成元素 | createElement+append |
| 删除元素 | 移除元素 | remove() |
| 事件绑定 | 响应用户操作 | addEventListener |
| 事件优化 | 动态元素事件 | 事件委托 |
核心记忆点:
- 用选择器方法获取元素
- 用
classList操作样式 - 用
addEventListener绑定事件 - 动态元素用事件委托
本文是"前端三基石"系列第二篇的续篇。下一篇将讲解异步编程与网络请求。
