DOM事件模型和事件委托

642 阅读3分钟

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
}