DOM事件进阶
事件流
事件流与两个阶段说明
概念
事件流:指的是事件完整执行过程中的流动路径
假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
捕获阶段是 从父到子 冒泡阶段是从子到父
实际工作都是使用事件冒泡为主
事件捕获
概念
从DOM的根元素开始去执行对应的事件 (从外到里)
语法:事件源.addEventListener('事件方式',事件处理函数,是否使用捕获机制)
addEventListener第三个参数传入 true 代表是捕获阶段触发(很少使用)
若传入false代表冒泡阶段触发,默认就是false
若是用 L0 事件监听(on事件监听),则只有冒泡阶段,没有捕获
<div> <p></p> </div> <script> // 事件监听:事件源.addEventListener('事件方式',事件处理函数,是否使用捕获机制) // 事件流:事件触发后的流程 // 捕获阶段 true // 冒泡阶段 false 默认 const div = document.querySelector('div') const p = document.querySelector('p') div.addEventListener('click', function () { console.log('div') },true) p.addEventListener('click', function () { console.log('p') },true) </script>
事件冒泡
概念
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的 同名事件
L0事件监听,L2事件监听默认都是冒泡
<div> <p></p> </div> <script> const div = document.querySelector('div') const p = document.querySelector('p') div.addEventListener('click', function () { console.log('div') }) p.addEventListener('click', function () { console.log('p') }) </script>
阻止冒泡
阻止事件冒泡
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
若想把事件就限制在当前元素内,就需要阻止事件冒泡
阻止事件冒泡需要拿到事件对象
语法:事件对象.stopPropagation()
此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
p.addEventListener('click', function (e) { console.log('p') // 阻止事件冒泡 e.stopPropagation() })
阻止默认行为
我们某些情况下需要阻止默认行为的发生,比如 阻止 链接的跳转,表单域跳转
语法:事件对象.preventDefault()
<a href="http://www.baidu.com">链接</a> <form action="http://www.baidu.com"> <input type="submit" value="提交"> </form> <script> const a = document.querySelector('a') const form = document.querySelector('form') a.addEventListener('click', function (e) { // 阻止a默认跳转行为 // 事件对象.preventDefault() 阻止默认 e.preventDefault() }) form.addEventListener('click', function (e) { // 阻止表单默认提交行为 e.preventDefault() }) </script>
解绑事件
传统on注册(L0)
绑定事件:事件源.onxxxx = function () {}
解绑事件: 事件源.onxxxx = null
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖偶就可以实现事件的解绑
- 都是冒泡阶段执行的,无法开启捕获阶段
<button>点击</button> <script> // 绑定事件 btn.onclick = function () { console.log('onclick1') } btn.onclick = function () { console.log('onclick2') } // 解绑事件 btn.onclick = null </script>
事件监听注册(L2)
绑定事件:事件源.addEventListener('事件类型', 处理函数)
解绑事件:事件源.removeEventListener('事件类型', 处理函数名)
- 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
- 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
- 必须使用removeEventListener('事件类型', 事件处理函数名, 是否开启捕获阶段)
- 匿名函数无法被解绑
<button>点击</button> <script> const f1 = function () { console.log('addEventListener1') } const f2 = function () { console.log('addEventListener2') } // 绑定事件 // 匿名函数,无法解绑 btn.addEventListener('click', function () { console.log('addEventListener0') }) btn.addEventListener('click', f1) btn.addEventListener('click', f2) // 解绑事件 // 事件源.removeEventListener('事件类型', 事件处理程序的名称) btn.removeEventListener('click', f2) </script>
事件委托
概念
事件委托:把事件委托给上级元素,利用事件流的特征解决一些开发需求的知识技巧
优点:
减少注册次数,可以提高程序性能,动态创建的元素也可以有事件了
原理:
事件委托其实是利用事件冒泡的特点。 给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
实现:
事件对象.target:最先触发事件的元素 ,标签
事件对象.target. tagName :可以获得真正触发事件的元素 ,返回大写的标签名
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10 <a href="">链接</a> </li> </ul> <script> // 事件委托:把事件委托给上级元素 /* 步骤: 1.给上级元素注册事件 2.利用事件对象.target找到最先触发元素 3.用tagName查看是否是我们要找的元素 */ const ul = document.querySelector('ul') // 注册事件 ul.addEventListener('click', function (e) { // e.target有可能不是我们要找的元素,只有我们要找的元素才可以 if (e.target.tagName === 'LI') { e.target.style.background = 'pink' } }) </script>
事件委托步骤:
1.给上级元素注册事件
2.利用事件对象.target找到最先触发元素
3.用tagName查看是否是我们要找的元素
其他事件
页面加载事件
load
事件名:load
触发方式:加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
使用场景:监听页面所有资源加载完毕: 给 window 添加 load 事件
<script> // 加载事件:load // 当外部资源加载完成后触发的事件 // 只要涉及带有路径的,都涉及加载 // load:所有资源加载 绑定在window window.addEventListener('load', function () { console.log('页面所有资源加载完成后执行') const img = document.querySelector('img') console.log(img) }) </script> <img src="./images/b01.jpg" alt=""> </body>
注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
<img src="./images/b01.jpg" alt=""> <script> // 加载事件:load // 当外部资源加载完成后触发的事件 // 只要涉及带有路径的,都涉及加载 const img = document.querySelector('img') img.addEventListener('load', function () { console.log('图片加载完成后执行') }) </script> </body>
DOMContentLoaded
事件名:DOMContentLoaded
触发方式:当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载
使用场景: 监听页面DOM加载完毕: 给 document 添加 DOMContentLoaded 事件
<script> // DOM加载:当初始的HTML文档加载 绑定在document document.addEventListener('DOMContentLoaded', function () { console.log('当初始的HTML文档被完全加载和解析完成之后执行') const img = document.querySelector('img') console.log(img) }) </script> <img src="./images/b01.jpg" alt=""> </body>
滚动事件
绑定/添加滚动事件
事件名:scroll
触发方式:滚动条在滚动的时候持续触发的事件
使用场景: 谁有滚动条,给谁加滚动事件
- 监听某个元素的内部滚动直接给某个元素加即可
- 监听整个页面滚动: 给 window 或 document 添加 scroll 事件
<style> div { width: 200px; height: 600px; background-color: #eee; overflow-y: scroll; } body { height: 2000px; } </style>
<script> // scroll:滚动事件 谁有滚动条,给谁加滚动事件 const div = document.querySelector('div') div.addEventListener('scroll', function () { console.log(`向上滚动${div.scrollTop}px`) }) // 如果整个页面有滚动条,加给widow window.addEventListener('scroll', function () { console.log('滚动ing...') }) </script>
获取位置
scrollLeft和scrollTop (属性)
使用场景:
- 在scroll事件里面获取被卷去的距离
- 获取被卷出的大小, 获取元素内容往左、往上滚出去看不到的距离
这两个值是可读写的,不仅可以获取,还可以设置
<script> const div = document.querySelector('div') div.addEventListener('scroll', function () { console.log(`向上滚动${div.scrollTop}px`) }) window.addEventListener('scroll', function () { // 整个页面再往出卷,那么卷的是html的scrollTop // 获取html // const html= document.documentElement const html = document.querySelector('html') console.log(`向上滚动${html.scrollTop}px`) }) // scrollTop不仅可以获取,还可以设置 const btn = document.querySelector('input') btn.addEventListener('click', function () { // 返回顶部,设置上卷距离为0 document.documentElement.scrollTop = 0 }) </script>
滚动到指定的坐标
语法:元素.scrollTo(x, y)
使用场景:把内容滚动到指定的坐标
btn.addEventListener('click', function () { // 返回顶部,设置上卷距离为0 // document.documentElement.scrollTop = 0 window.scrollTo(0,0) })
页面尺寸事件
绑定/添加滚动事件
事件名:resize
触发方式:窗口尺寸改变的时候触发事件
使用场景: 检测屏幕宽度: 给 window 添加 resize 事件
window.addEventListener('resize', function () { const w = document.documentElement.clientWidth console.log(`当前页面宽度:${w}`) })
元素尺寸与位置
获取元素宽高
属性名:clientWidth和clientHeight
使用场景:获取元素的可见部分宽高:内容 + padding(不包含边框,margin,滚动条等)

属性名:offsetWidth和offsetHeight
使用场景:获取元素的自身宽高,包含元素自身设置的宽高、padding、border
注意
- 获取出来的是数值,方便计算
- 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
- 以上两种属性只能获取,不能设置
- 获取页面宽高,通常使用clientWidth和clientHeight,获取元素宽高,通常使用:offsetWidth和offsetHeight
<style> div { width: 200px; height: 200px; padding: 10px; margin: 10px; border: 10px solid #000; } </style>
<div></div> <script> const div = document.querySelector('div') div.style.width // 获取宽高 // 获取页面宽高使用clientWidth // clientWidth:包含:内容宽 + padding console.log(div.clientWidth,div.clientHeight) // offsetWidth:包含:内容宽 + padding + border // 获取元素宽高:offsetWidth console.log(div.offsetWidth,div.offsetHeight) </script>
获取位置
属性名:offsetLeft和offsetTop
使用场景:获取元素距离自己定位父级元素的左、上距离,参照定位元素的位置距离
方法名:getBoundingClientRect
语法:element.getBoundingClientRect()
返回值:元素的大小及其相对于视口的位置
注意:
- offsetLeft和offsetTop 注意是只读属性,只能获取,不能设置
- offsetLeft和offsetTop 以带有定位的父级为参考坐标,如果上级中都没有定位则以文档左上角 为准
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)