22.1 事件捕获和事件冒泡
起源是啥?
看源码:
<div id="father"><button id="child"></button></div>
button是儿子, 被div爸爸包裹着.
问题来了?: 如果我点击了button, 我是先响应div爸爸的事件, 还是先响应button儿子事件呢?
微软就说: 我觉得应该先响应儿子, 再响应爸爸, 称之为事件冒泡
网景就说: 老子看不惯微软, 我要反着来: 称之为事件捕获
两人就闹到了w3c.
w3c作为和事佬就说: 浏览器应该支持两种顺序, 首先按照事件捕获顺序看有没有事件监听, 再按照事件冒泡.
那岂不是所有函数都要执行两遍?
不是的, 事件捕获, 和事件冒泡就像两个独立的容器, 开发者自由选择将事件放在哪个容器.
这里说明w3c有个小心眼, 偷偷的赞同微软的事件冒泡: w3c的api 默认将函数放到事件冒泡的容器, 除非开发者将那个bool参数设置为true
baba.addEventListener('click', fn, bool)
可以在setTimeout中调用事件e吗?
不可以, 因为在语句执行完的时候, e会消亡(具体来说,是会被浏览器偷偷修改,修改的边界条件很复杂). 所以我们可以将e中需要的信息保存下来
target和currentTarget的区别?
e.target: 用户操作的元素
e.currentTarget: 被程序员监听的函数.
例如:
<div id="father" onClick = {监听事件}>
<button id="child">请点击这个按钮</button>
</div>
button被div包裹着, div被程序员监听着, button就是e.target, div就是e.currentTarget
事件捕获和事件冒泡可不可以取消?
捕获不可取消, 冒泡可以取消
可以设置.e.stopPropagation. 为true. 当一个独立组件说: 出了什么问题找我就行, 不要告诉我爸爸
有哪些时间不可取消冒泡
例如滚动scroll不可取消冒泡, 其他的可以在mdn中查看
如果非要阻止滚动:
- 阻止滚轮
- 然后隐藏滚动条
- 禁止触屏
22.2 Dom事件
浏览器有100多个事件, 在mdn中有列表, 还可以自定义事件
如何自定义事件?
button1.addEventListener('click',()=>{
const event = new CustomEvent('frank',{
detail: {name:'frank', age:18},
bubble:true, //是否会向上冒泡, 如何false,就不会告诉爸爸
cancelable:false; //是否可以取消冒泡
})
button1.dispatchEvent(event)
})
div1.addEventListener('frank', (e)=>{
console.log("frank 事件触发了")
console.log(e.detail)
})
22.3 事件委托
简单来说:委托别的元素帮我监听.
什么时候会使用事件委托?
在两种情况下使用:
-
要给100个按钮添加事件,对包裹的元素增加监听: 省内存
-
我想监听不存在的元素: 我先监听祖先: 监听动态事件
matchs: 一个元素是不是button
如何实现事件委托?
//element: 事件委托的元素, 也就是被监听的爸爸, 如div
//selector: 用户操作的元素, 也就是被操作的儿子, 如button
function on(eventType, element, selector, fn){
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType, (e)=>{
const t=e.target
if(t.matches(selector)){//matches 就是判断是某个元素吗
const t = e.target
}
})
}
上面实现的事件监听会有什么问题?
button里面加一个孩子span, 点击无效. 因为改变了e.target了,但是没有改变selector 解决方案:
function on(eventType, element, selector, fn){
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType, (e)=>{
const el=e.target
while(!el.matches(selector)){
if (element === el){ //如果找到div,也就是e.currentTarget都还没有找到button,那就放弃吧
el = null
break
}
el = el.parentNode//当前e.target不对,那就一直往上找祖先,有没有button
}
el && fn.call(el, e, el)
})
return element
}
js支持Dom事件吗?
js不支持事件, Dom事件只是浏览器提供的功能. js只是调用了Dom的addEventListener