JavaScript加强篇——第三章 事件大全(完整版)
目录
一、事件监听
二、事件监听三要素
三、事件监听版本
四、事件类型
五、事件对象
六、环境对象 (this)
七、回调函数
八、事件流
九、事件捕获
十、事件冒泡
十一、阻止事件冒泡
十二、解绑事件
十三、两种注册事件的区别
十四、鼠标经过事件的区别
十五、事件委托
十六、事件委托
十七、页面加载事件
十八、页面滚动事件
十九、页面尺寸事件
二十、元素尺寸与位置
二十一、滚动位置属性对比
此章节主要讲解:事件监听、事件监听三要素、事件监听版本、事件类型、事件对象、环境对象 (this)、回调函数、事件流、事件捕获、事件冒泡、阻止事件口泡、解绑事件、两种注册事件的区别、鼠标经过事件的区别、事件委托、事件委托、页面加裁事件、页面滚动事件、页面尺寸事件、元素尺寸与位置、滚动位置属性对比。
一、事件监听
核心概念
-
事件:程序运行时发生的动作或事情(如用户点击按钮)
-
事件监听:让程序检测事件是否发生,触发时立即调用响应函数
代码示例
const btn = document.querySelector('button');
btn.addEventListener('click', function() {alert('按钮被点击了!');
});
⚠️ 注意事项
-
事件类型必须加引号:
'click'
✅ 正确 vsclick
❌ 错误 -
每次触发事件都会执行一次回调函数
二、事件监听三要素
三要素组成
-
事件源:被触发的DOM元素
-
事件类型:触发方式(click/mouseover等)
-
事件处理函数:触发后执行的逻辑
关系图解
三、事件监听版本
两种注册方式
版本 | 语法 | 特点 |
---|---|---|
DOM L0 | 元素.onclick = function | 绑定会被覆盖 |
DOM L2 | addEventListener() | 可绑定多个事件处理函数 |
❗ 使用原则
始终使用
addEventListener
,避免使用on事件
方式
四、事件类型
常用事件类型
类别 | 事件类型 | 触发条件 |
---|---|---|
鼠标事件 | click | 鼠标点击 |
mouseenter | 鼠标进入元素区域 | |
mouseleave | 鼠标离开元素区域 | |
焦点事件 | focus | 元素获得焦点 |
blur | 元素失去焦点 | |
键盘事件 | keydown | 键盘按键按下 |
keyup | 键盘按键释放 | |
文本事件 | input | 输入框内容变化 |
五、事件对象
核心概念
-
事件对象:事件触发时自动创建,包含事件相关数据
-
获取方式:回调函数的第一个参数(通常命名为
e
)
常用属性
属性 | 说明 |
---|---|
e.type | 事件类型(如 'click') |
e.clientX/Y | 光标相对于浏览器窗口的位置 |
e.offsetX/Y | 光标相对于当前元素的位置 |
e.key | 按下的键盘键值(推荐使用) |
⚠️ 注意事项
键盘事件中优先使用
e.key
而不是已废弃的keyCode
六、环境对象 (this)
核心概念
-
this:函数内部特殊变量,代表当前运行环境
-
判断规则:谁调用函数,
this
就指向谁
示例对比
// 普通函数:this指向调用者
btn.addEventListener('click', function() {console.log(this); // 指向btn元素
});// 箭头函数:this指向定义时的环境
btn.addEventListener('click', () => {console.log(this); // 指向window(通常不是期望结果)
});
七、回调函数
核心概念
-
回调函数:作为参数传递给另一个函数的函数
-
常见场景:事件处理、定时器、异步操作
正确用法
// 命名函数(可解绑)
function handleClick() {console.log('点击处理');
}
btn.addEventListener('click', handleClick);// 匿名函数(无法解绑)
btn.addEventListener('click', function() {console.log('匿名回调');
});
⚠️ 易错点
传递函数时使用函数名不加括号:
✅setInterval(callbackFn)
❌setInterval(callbackFn())
(会立即执行)
八、事件流
核心概念
-
事件流:事件完整执行过程中的流动路径
-
两个阶段:
-
捕获阶段:从父元素到子元素(从外到内)
-
冒泡阶段:从子元素到父元素(从内到外)
-
❗ 重要原则
实际开发中主要使用冒泡阶段(默认行为)
九、事件捕获
启用方式
元素.addEventListener(事件类型, 处理函数, true);
// 第三个参数为true表示捕获阶段触发
特点
-
从DOM根元素开始执行(从外到里)
-
需要显式设置才能观察到效果
-
DOM L0 不支持捕获(只有冒泡)
十、事件冒泡
核心概念
-
事件冒泡:事件触发后,会依次向上调用所有父元素的同名事件
-
默认存在:所有事件监听默认在冒泡阶段触发
示例
<div class="爷爷">爷爷<div class="爸爸">爸爸<div class="儿子">儿子</div></div>
</div><script>
document.querySelector('.爷爷').addEventListener('click', () => alert('爷爷'));
document.querySelector('.爸爸').addEventListener('click', () => alert('爸爸'));
document.querySelector('.儿子').addEventListener('click', () => alert('儿子'));
// 点击"儿子"会依次触发:儿子 → 爸爸 → 爷爷
</script>
十一、阻止事件冒泡
使用场景
需要将事件限制在当前元素内,不影响父元素
实现方法
元素.addEventListener('click', function(e) {e.stopPropagation(); // 阻止事件继续传播// 其他逻辑...
});
⚠️ 注意
此方法在捕获和冒泡阶段都有效
十二、解绑事件
两种解绑方式
1. DOM L0 方式
btn.onclick = function() { /* 逻辑 */ };
// 解绑
btn.onclick = null;
2. DOM L2 方式
function handleClick() { /* 逻辑 */ }// 绑定
btn.addEventListener('click', handleClick);
// 解绑
btn.removeEventListener('click', handleClick);
❗ 关键区别
匿名函数无法被解绑,始终使用命名函数以便解绑
十三、两种注册事件的区别
特性 | DOM L0 (on事件) | DOM L2 (addEventListener) |
---|---|---|
覆盖问题 | 后注册会覆盖前注册 | 可注册多个处理函数 |
解绑方式 | 元素.on事件 = null | removeEventListener |
阶段控制 | 仅冒泡阶段 | 可通过参数控制捕获/冒泡阶段 |
匿名函数 | 可解绑 | 匿名函数无法解绑 |
十四、鼠标经过事件的区别
事件类型 | 冒泡效果 | 推荐场景 |
---|---|---|
mouseover | 有 | 不推荐 |
mouseout | 有 | 不推荐 |
mouseenter | 无 | ✅ 推荐使用 |
mouseleave | 无 | ✅ 推荐使用 |
十五、事件委托
解决痛点
当需要给多个相似元素添加事件时,避免循环绑定
实现原理
<ul id="list"><li>选项1</li><li>选项2</li><li>选项3</li>
</ul><script>
document.getElementById('list').addEventListener('click', function(e) {if(e.target.tagName === 'LI') {console.log('点击了:', e.target.textContent);}
});
</script>
核心优势
-
性能优化:只需绑定一个事件监听器
-
动态元素:自动处理新增的子元素
-
内存节省:减少事件监听器数量
✅ 事件系统核心要点总结
图表
代码
黄金实践原则:
始终使用
addEventListener
(DOM L2)事件处理函数使用命名函数以便解绑
鼠标事件使用
mouseenter/mouseleave
批量元素处理使用事件委托
需要阻止冒泡时使用
e.stopPropagation()
十六、事件委托
核心概念
利用事件冒泡特性,将子元素的事件委托给父元素处理
三大优势
-
减少注册次数:只需绑定一个父元素
-
提高性能:节省内存资源
-
支持动态元素:自动处理新增子元素
实现原理
<ul id="list"><li>选项1</li><li>选项2</li><li>选项3</li>
</ul><script>
document.getElementById('list').addEventListener('click', function(e) {// 检测实际点击的元素if(e.target.tagName === 'LI') {console.log('点击了:', e.target.textContent);e.target.style.backgroundColor = 'pink';}
});
</script>
关键技巧
// 精确判断被点击的子元素
if(e.target.tagName === 'LI') { /* 处理逻辑 */ }// 或使用类名判断
if(e.target.classList.contains('item')) { /* 处理逻辑 */ }
十七、页面加载事件
两种加载事件对比
事件类型 | 触发时机 | 绑定对象 | 适用场景 |
---|---|---|---|
load | 页面所有资源加载完毕(包括图片等) | window | 需要操作图片/外部资源时 |
DOMContentLoaded | HTML解析完成(无需等样式/图片) | document | 需要尽早操作DOM元素时 |
代码实现
// 1. load事件(等待所有资源)
window.addEventListener('load', function() {console.log('所有资源加载完成');// 可安全操作图片尺寸等
});// 2. DOMContentLoaded(推荐)
document.addEventListener('DOMContentLoaded', function() {console.log('DOM解析完成');// 可立即操作DOM元素const btn = document.querySelector('button');btn.disabled = false;
});
⚠️ 常见问题解决
脚本放在head中无法获取元素?
使用DOMContentLoaded事件包裹操作:document.addEventListener('DOMContentLoaded', function() {// 这里可以安全获取DOM元素const element = document.getElementById('content'); });
✅ 事件系统核心要点总结
黄金实践原则:
动态元素处理使用事件委托
DOM操作使用 DOMContentLoaded
资源相关操作使用 load
表单处理记得 preventDefault()
事件传播控制使用 stopPropagation()
📝 高频面试题速答
-
Q:事件委托有什么好处?
A:减少注册次数提高性能,支持动态元素,节省内存资源
-
Q:如何找到实际触发事件的元素?
A:使用
e.target.tagName
或e.target.classList
-
Q:load 和 DOMContentLoaded 的区别?
A:load 等待所有资源加载,DOMContentLoaded 只需HTML解析完成
-
Q:如何阻止表单自动提交?
A:在submit事件中使用
e.preventDefault()
-
Q:事件冒泡有什么应用场景?
A:事件委托就是利用冒泡机制实现的
十八、页面滚动事件
核心概念
-
scroll事件:当页面或元素滚动时触发
-
使用场景:
-
滚动到指定位置显示/隐藏元素
-
实现滚动进度指示器
-
懒加载图片等资源
-
获取滚动位置
// 1. 监听整个页面滚动
window.addEventListener('scroll', function() {// 获取页面垂直滚动距离(被卷去的头部)const scrollTop = document.documentElement.scrollTop;console.log('页面滚动距离:', scrollTop);// 示例:滚动超过100px显示返回顶部按钮if(scrollTop > 100) {backToTopBtn.style.display = 'block';}
});// 2. 监听元素内部滚动
const div = document.querySelector('.scrollable');
div.addEventListener('scroll', function() {// 获取元素内部滚动位置console.log('元素垂直滚动:', this.scrollTop);console.log('元素水平滚动:', this.scrollLeft);
});
⚠️ 注意事项
页面滚动距离通过
document.documentElement.scrollTop
获取元素内部滚动通过元素的
scrollTop
属性获取这些属性可读写,可通过赋值修改滚动位置
十九、页面尺寸事件
核心概念
-
resize事件:当窗口尺寸改变时触发
-
使用场景:
-
响应式布局调整
-
移动端横竖屏切换
-
图表等可视化组件重绘
-
获取窗口尺寸
window.addEventListener('resize', function() {// 获取视口宽度(不含滚动条)const viewportWidth = document.documentElement.clientWidth;// 获取视口高度(不含滚动条)const viewportHeight = document.documentElement.clientHeight;console.log(`窗口尺寸: ${viewportWidth} x ${viewportHeight}`);// 示例:移动端布局调整if(viewportWidth < 768) {navbar.classList.add('mobile-mode');} else {navbar.classList.remove('mobile-mode');}
});
⚠️ 注意事项
clientWidth/clientHeight
获取的是可视区域尺寸,不包含滚动条
二十、元素尺寸与位置
1. 尺寸属性
属性 | 包含内容 | 是否只读 | 使用场景 |
---|---|---|---|
offsetWidth | 内容+padding+border | 只读 | 获取元素完整宽度 |
offsetHeight | 内容+padding+border | 只读 | 获取元素完整高度 |
clientWidth | 内容+padding(不含边框) | 只读 | 获取可视内容宽度 |
clientHeight | 内容+padding(不含边框) | 只读 | 获取可视内容高度 |
2. 位置属性
属性 | 说明 | 基准点 |
---|---|---|
offsetTop | 元素顶部距离定位父元素的距离 | 最近的定位父元素 |
offsetLeft | 元素左侧距离定位父元素的距离 | 最近的定位父元素 |
scrollTop | 元素内容垂直滚动距离 | 元素顶部 |
scrollLeft | 元素内容水平滚动距离 | 元素左侧 |
3. 获取元素位置方法
// getBoundingClientRect() 返回元素相对于视口的位置
const rect = element.getBoundingClientRect();
console.log(`元素位置: top: ${rect.top}px, left: ${rect.left}px,width: ${rect.width}px,height: ${rect.height}px
`);// 获取元素相对于文档的位置
const docTop = rect.top + window.scrollY;
const docLeft = rect.left + window.scrollX;
⚠️ 关键注意事项
-
隐藏元素的尺寸获取结果为0
-
offsetTop/offsetLeft
的基准是最近的定位父元素 -
若无定位父元素,则相对于文档左上角计算
-
getBoundingClientRect()
的值随滚动实时变化
二十一、滚动位置属性对比
属性 | 作用 | 是否可修改 | 适用对象 |
---|---|---|---|
scrollTop | 获取/设置垂直滚动距离 | ✅ 可读写 | 页面/可滚动元素 |
scrollLeft | 获取/设置水平滚动距离 | ✅ 可读写 | 页面/可滚动元素 |
pageYOffset | 获取页面垂直滚动距离 | ❌ 只读 | window (等同于scrollTop) |
pageXOffset | 获取页面水平滚动距离 | ❌ 只读 | window |
示例:平滑滚动到顶部
// 方式1:修改scrollTop
function scrollToTop() {const top = document.documentElement.scrollTop;if(top > 0) {window.scrollTo(0, top - 50); // 每次上移50pxrequestAnimationFrame(scrollToTop);}
}// 方式2:使用scrollTo API
backToTopBtn.addEventListener('click', () => {window.scrollTo({top: 0,behavior: 'smooth' // 平滑滚动});
});
✅ 核心要点总结
📝 高频面试题速答
-
Q:如何获取页面滚动距离?
A:
document.documentElement.scrollTop
-
Q:offsetWidth和clientWidth的区别?
A:offsetWidth包含边框,clientWidth不包含边框
-
Q:如何让元素滚动到指定位置?
A:设置元素的
scrollTop
属性或使用window.scrollTo()
-
Q:getBoundingClientRect()获取的位置是否包含滚动距离?
A:否,它返回相对于视口的位置,需加上scrollY/scrollX获取文档位置
-
Q:如何监听窗口尺寸变化?
A:使用
window.addEventListener('resize', callback)
🧠 记忆口诀
"三尺寸四位置,滚动监听要牢记"
三尺寸:offset、client、scroll
四位置:offsetTop/Left、scrollTop/Left
两事件:scroll、resize