1.事件模型
-
我们知道,DOM是个树形结构,当我们在页面上单击一个按钮,页面上哪些元素会触发这个事件,是发生在这个按钮上,还是这个按钮的容器元素(我们说父元素)也会触发这个点击事件呢?这就牵扯到事件流,进过思考,我们知道它描述的是事件触发顺序,那么是按钮和其容器元素都触发吗,它们谁先触发呢?这可不是确定的,得看是哪种类型的事件流了。
-
我们通过一个模型来理解

-
从图中我们可以看到事件模型中的三个阶段(按顺序执行)
1.捕获阶段
2.目标阶段
3.冒泡阶段
2.事件绑定api
<div class="a">
<div class="b">
文字
</div>
</div>
a.adEventListener('click',fn,bool)
fn为一个函数,如果bool不传或者值为falsy时,就会fn走冒泡,即当浏览器在冒泡阶段发现a有fn监听函数,就会调用fn,并提供事件信息。如果bool为true,就让fn走捕获,即当浏览器在捕获阶段发现a有fn监听函数,就会调用fn,并提供事件信息。
注意事项如果直接监听是监听不到事件的,因为1s后,事件已经结束,所以我们需要将监听的事件记录下来

3.target和currentTarget
它们的区别在于:target是用户操作的元素,currentTarget是程序员监听的元素 比如:
<div>
<span>文字</span>
</div>
//e.target就是span
//e.currentTarget就是div
4.如何取消冒泡
需要注意的是:捕获是不可取消的,但是冒泡是可以取消的
e.stopPropagation()//可以中断冒泡,一般用于封装某些独立的组件
5.自定义事件
<div>
<button id="button1"></button>
</div>
button1.addElementListener('click',()=>{
const event = new CustomEvent('huo',{
detail:{name:'huo',age:2}
}) // 声明一个事件,事件名和事件信息
button1.dispatchEvent(event) //触发这个事件
})
button1.addElementListener('huo',(e)=>{
console.log(e.detail) //监听这个事件
})
6.事件委托
事件委托就是监听祖先元素,等我们点击时来判断它是不是我们要监听的元素,是的话执行函数,事件委托的优点是:节省内存,可以监听动态元素。
事件封装形式如下:
function on(eventType,element,selector,fn){
if(!(element instance Element)){
element = document.querySelector(element) //判断如果 element不是元素就去找那个元素
}
element.addEventListener(eventType,()=>{
const t = e.target //t等于被操的那个元素
if(t.matches(selector)){ //判断t是否匹配selector,
fn(e)
}
})
}
on('click','#div','butotn',()=>{
console.log("btton被点击")
})
上面代码中存在一个问题,就是如果被操的元素(target)不一定是我们要监听的元素,所以我们需要使用递归来完善一下。
function on(eventType,element,selector,fn){
if (!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType,(e)=>{
let t = e.target
while(t.tagName.toLowercase() !== selector){
if(t === element){
t = null
break
}
t = t.parentNode
}
t && fn.call(t,e,t)
})
return element
}