js事件机制
页面上点击一个元素,这个点击动作会按照固定顺序传递,JS 事件机制就是规定**事件从哪开始、往哪走、什么时候触发函数的一套规则。
类比:一张桌子,桌子上放一个杯子。你用手拍杯子。
✅ 默认
addEventListener('click',fn),是在冒泡阶段 触发 ✅addEventListener('click',fn,true)第三个参数写 true,代表在捕获阶段触发
二、解决什么问题?
三、常用场景 + 代码示例
场景 1:事件冒泡(默认现象)
盒子里面有按钮,点击按钮,按钮和外层盒子的点击事件都会执行
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<div id="father" style="padding:30px;background:skyblue;">
父盒子
<button id="son">点我</button>
</div>
<script>
let father = document.querySelector('#father')
let son = document.querySelector('#son')
father.addEventListener('click', ()=>{
console.log('父元素被点击')
})
son.addEventListener('click', ()=>{
console.log('按钮被点击')
})
</script>
</body>
</html>
运行结果:点按钮 → 先打印按钮被点击,然后打印父元素被点击。 原因:触发按钮事件后,事件向上冒泡到父 div。
阻止冒泡写法:e.stopPropagation()
son.addEventListener('click', (e)=>{
e.stopPropagation() // 阻止事件向外冒泡
console.log('按钮被点击')
})
加上这行,点击按钮,只会执行按钮事件,父盒子不会触发。
场景 2:事件委托(事件代理,最常用!)
解决问题 :子元素很多,或者子元素是动态新增 的,不用逐个绑定事件,只给父元素绑定一次。 原理:利用事件冒泡,点击子元素,事件会冒泡到父元素,父元素统一处理。
例子:ul 里面很多 li,后续还会新增 li
<ul id="list">
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
</ul>
<button id="add">新增项目</button>
<script>
let list = document.querySelector('#list')
let addBtn = document.querySelector('#add')
// 只给父ul绑定事件,利用冒泡
list.addEventListener('click', (e)=>{
// e.target 就是真正点击到的元素
if(e.target.tagName === 'LI'){
console.log('你点击了:',e.target.innerText)
}
})
// 动态新增li
addBtn.addEventListener('click', ()=>{
let li = document.createElement('li')
li.innerText = '新项目'
list.appendChild(li)
})
</script>
优点:
- 减少事件绑定,节省内存,性能更好
- 动态添加的元素自动拥有事件,不用重新绑定(这个是最大优势)
场景 3:阻止默认行为 e.preventDefault()
不是阻止冒泡!是阻止浏览器自带的默认动作 例子:a 标签点击默认跳转,表单 submit 默认提交刷新页面
<a href="https://www.baidu.com" id="link">百度</a>
<script>
let link = document.querySelector('#link')
link.addEventListener('click', (e)=>{
e.preventDefault() // 阻止a标签默认跳转
console.log('链接点击,不会跳转')
})
</script>
四、两个容易混淆 API 区分(考试常考)
五、总结思维导图版(方便写作业)
六、常见坑
-
捕获阶段:从最外层(桌子)→向内(杯子),从上往下传递事件
-
冒泡阶段 :从被点击的目标(杯子)→向外(桌子),从里往外冒事件 JS 默认:事件冒泡一、事件流三个阶段
-
捕获阶段 :事件从 window → document → html → body → 外层父元素,一层一层向内走,还没到达点击的目标元素
-
目标阶段:事件到达你真正点击的那个元素,触发这个元素绑定的事件
-
冒泡阶段:事件从目标元素,一层一层向外传递给父元素,直到 window
-
解决嵌套元素点击冲突:比如 div 里面套按钮,点按钮,父 div 的点击事件也跟着触发(冒泡带来的现象)
-
实现事件委托(事件代理):不用给每一个子元素绑定事件,只绑定父元素,利用冒泡,提升性能
-
可以手动控制:阻止事件继续传播(
e.stopPropagation()),不让事件继续捕获 / 冒泡 -
e.stopPropagation():停止事件传播,阻止捕获 / 冒泡继续走,不会阻止元素本身事件 -
e.preventDefault():阻止浏览器默认行为,不影响事件冒泡 -
return false:jQuery 里,同时阻止冒泡 + 默认行为;原生 JS 只能阻止默认行为,不能用来阻止冒泡(别搞混) -
JS 事件机制 = 事件流:捕获 → 目标 → 冒泡
-
默认 addEventListener 是冒泡阶段触发
-
冒泡:子触发,往上传给父;捕获反过来
-
核心应用:事件委托(事件代理),适合列表、动态元素
-
常用 API:
e.stopPropagation():阻止冒泡 / 捕获传播e.preventDefault():阻止浏览器默认行为e.target:真正点击的目标元素(事件委托必用)
-
事件委托要绑定在离目标元素最近的父元素,不要绑到 body/window,避免不必要事件触发
-
e.target和this的区别:e.target:实际点击到的元素- 事件处理函数里的
this:绑定事件的那个元素(父元素)