代码
- HTML
<div class=爷爷>
<div class=爸爸>
<div class=儿子>
文字
</div>
</div>
</div>
给这三个div分别添加事件监听,fnYe、fnBa、fnEr
- 提问1 点击了谁
当我们点击 文字 时,算不算点击儿子?算不算点击爸爸?算不算点击爷爷?
答案:都算
- 提问2 调用顺序
点击文字,最先调用哪个函数
答案:都行
IE5认为先调用fnEr,网景认为先调用fnYe
2002年,W3C发布标准
规定浏览器必须同时支持两种调用顺序:
浏览器首先按照 爷爷=>爸爸=>儿子 顺序查查看有没有函数监听
然后按照 儿子=>爸爸=>爷爷 的顺序看有没有函数监听
有函数监听就调用,并提供事件信息,没有就跳过
术语:
由内向外找监听函数,叫事件冒泡
由外向内找监听函数,叫事件捕获
示意图:
addEventlistener
事件绑定API
IE5*:baba.attachEvent('onclick',fn) //冒泡
网景:baba.addEventListener('click',fn) //捕获
W3C:baba.addEventListener('click',fn,bool)
如果 bool 传或为 false
就让fn走冒泡,浏览器在冒泡baba有fn监听函数,就会调用fn
如果 bool 为 true
就让fn走捕获。。。
代码示例
注意 e对象被传给所有监听函数,事件结束后,e对象就不存在了。e.target 是用户操作的元素
取消冒泡 && 不可取消冒泡
捕获不可取消,但冒泡可以
e.stopPropagation()可中断冒泡,浏览器不再向上走。一般用于封装某些独立的组件
不可取消冒泡
MDN搜索 scroll event,看到Bubbles和Cancelacle
意思是不可取消冒泡
自定义事件
浏览器事件
一共100多种事件,列表在MDN上
用户可以自定义事件
事件委托
例如给100个按钮添加点击事件
监听这100个按钮的祖先,等冒泡的事件判断target是不是这100按钮中的11个
优点
省监听数(内存)
可以监听动态元素
封装事件委托
用户传入四个参数(事件,元素,用户操作的元素,回调函数)
on('click','#div1','button',()=>{
console.log('button 被点击了')
})
简洁版(有bug)
function on(eventType,element,selector,fn){
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType,(e)=>{
const t = e.target
if(t,maches(selector)){
fn(e)
}
})
}
完整版
function on(element, eventType, selector, fn) {
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
}