一、DOM事件机制
从外向内找监听函数,叫事件捕获
从内向外找监听函数,叫事件冒泡
- W#C事件模型:默认先捕获,再冒泡。
addEventListener
- 事件绑定 API
- IE 5*:
baba.attachEvent('onclick', fn) // 冒泡 - 网景:
baba.addEventListener('click',fn) // 捕获 - W3C:
baba.addEventListener('click', fn, bool)
- 如果 bool 不传或为 falsy
- 就让 fn 走冒泡,即当浏览器在冒泡阶段发现 baba 有 fn 监听函数,就会调用 fn,并提供事件信息
- 如果 bool 为 true
- 就让 fn 走捕获,即当浏览器在捕获阶段发现 baba 有 fn 监听函数,就会调用 fn ,并提供事件信息
target v.s. currentTarget
- 区别
e.target- 用户操作的元素e.currentTarget- 程序员监听的元素this是e.currentTarget,不推荐使用
- 举例
div > span{文字},用户点击文字e.target就是spane.currentTarget就是div
取消冒泡
- 捕获不可取消,但冒泡可以
e.stopPropagation()可中断冒泡,浏览器不再向上走- 一般用于封装某些独立的组件
不可阻止默认动作
- scroll 事件不可阻止默认动作
- MDN 搜索 scroll event,看到 Bubbles 和 Cancelable
- Bubbles 的意思是该事件是否冒泡,所有冒泡都可取消
- Cancelable 的意思是开发者是否可以阻止默认事件
- Cancelable 与冒泡无关
- 推荐看 MDN 英文版,中文版内容不全
如何阻止滚动
- 有些事件不可阻止默认动作
- 阻止 scroll 默认动作没用,因先有滚动才有滚动事件
- 要阻止滚动,可阻止 wheel 和 touchstart 的默认动作
- 注意你需要找准滚动条所在的元素
- 但是滚动条还能用,可用 CSS 让滚动条 width: 0
- CSS 也行
- 使用 overflow: hidden 可以直接取消滚动条
- 但此时 JS 依然可以修改 scrollTop
二、自定义事件
- 浏览器自带事件,一共100多种事件,列表在 (事件参考 | MDN (mozilla.org) 上
- 开发者能不能在自带事件之外,自定义一个事件?可以
三、事件委托
- 对"事件处理程序过多"问题的解决方案,就是需要触发子事件时,只用给某父元素或祖先元素指定一个事件处理程序,就可以管理某一类型的所有事件.事件委托原理原理就是事件冒泡原理.
- 场景一
- 给 100 个按钮添加点击事件
- 答:监听这 100 个按钮的祖先元素,等冒泡的时候判断 target 是不是这 100 个按钮中的一个
js代码
div1.addEventListener('click', (e)=>{
const t = e.target
if(t.tagName.toLowerCase() === 'button'){
console.log('button'被点击了)
console.log('button'内容是 + t.textContext) // 获取被点击元素的文本内容
console.log('button 的data-id是:'+ t.dataset.id) // 获取被点击元素的dataset.id
}
})
- 场景二
- 监听目前不存在的元素的点击事件
- 答:监听祖先元素,等点击的时候看看是不是我想要监听的元素即可
js代码
setTimeout(()=>{
const button = document.createElement('button')
button.textContent= 'click 1'
div1.appendChild(button)
},1000)
div1.addEventListener('click', (e)=>{
const t = e.target
if(t.tagName.toLowerCase() === 'button'){
console.log('button 被 click')
}
})
4.事件委托优点:
- 省监听数(内存)
- 可以监听动态元素
四、封装事件委托
- 要求写出这样一个函数
on('click', '#testDiv', 'li', fn)
- 当用户点击
#testDiv里的li元素时,调用fn函数 - 要求用到事件委托
- 答案一
- 判断 target 是否匹配 'li'
- 答案二
- 递归判断 target / target的爸爸 / target的爷爷