DOM 事件
使用 JS 操作 DOM,绕不开的就是事件这个概念。
2002年,W3C 发布标准,文档名为:DOM Level 2 Events Specification
规定浏览器应该同时支持两种调用顺序:
- 首先按 爷爷 => 爸爸 => 儿子 的顺序看有没有函数监听
- 然后按 儿子 => 爸爸 => 爷爷 的顺序看有没有函数监听
其中:
- 从外向内 找监听函数,叫 事件捕获
- 从内向外 找监听函数,叫 事件冒泡
注意:这并不代表事件会触发两次,开发者自己选择把事件放在捕获阶段还是放在冒泡阶段
/**
* type: 表示监听事件类型的字符串
* listener: 监听事件触发时的回调函数
* options: false 表示 listener 走冒泡; true 表示 listener 走捕获
*/
addEventListener(type, listener, options)
// 示例
dom.addEventListener('click', () => {
console.log('触发了点击事件')
})
target 和 currentTarget
- e.target: 用户操作的元素
- e.currentTarget: 程序员监听的元素
- this 是 e.currentTaget (不推荐使用)
取消冒泡事件
捕获事件不可取消,但冒泡事件可以: e.stopPropagation()
注意:有些时间不可取消冒泡
MDN 搜索 scroll event,看到 Bubbles 和 Cancelable
Bubbles 的意思是该事件是否冒泡
Cancelable 的意思是开发者是否可以取消冒泡
如何阻止页面的滚动
- 取消滚动元素的 wheel 和 touchstart 的默认动作
- 将滚动元素的滚动条用 css 隐藏
// 阻止PC端
dom.addEventListener('wheel', e => {
e.preventDefault()
})
// 阻止移动端
dom.addEventListener('touchstart', e => {
e.preventDefault()
})
.dom::-webkit-scrollbar {
width: 0 !important;
}
事件委托
场景1:给 100 个按钮添加点击事件
解决思路:监听这 100 个按钮的祖先,登报跑的时候判断 e.target 是不是这 100 个按钮中的一个
场景2:监听不存在的元素的点击事件
解决思路:监听上级元素,等点击的时候看是不是想要监听的元素即可
特点:减少事件数量,预言未来元素,避免内存外泄
/**
* 封装事件委托
* @param {String} eventType 事件名称
* @param {String} element 要监听的元素的选择器
* @param {String} selector 未来元素的选择器
* @param {Function} fn 事件触发时的回调函数
*/
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element)
}
element.addEventListener(eventType, e => {
let el = e.target
while (!el.matches(selector)) {
if (element === el) {
el = null
break
}
el = el.parentNode
}
el && fn.call(el, e, el)
})
return element
}