事件委托

·  阅读 107

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父结点上,由父节点的监听函数统一处理多个子元素的事件,这种方法叫事件委托。

事件委托的优点

  1. 减少内存消耗,提高性能

    对于如下代码

    <div class='buttons'>
        <button>按钮1</button>
        <button>按钮2</button>
        <button>按钮3</button>
        <button>按钮4</button>
    </div>
    复制代码

    现需要在点击每个 button 时响应一个事件,如果给每个 button 都绑定一个函数,那么会消耗较多的内存。借助事件委托,只需要将监听函数定义在父元素 div 上,这样不管点击的是哪一个 button 标签,遵循冒泡的传递机制,都会响应事件。

  2. 可以监听动态元素

    很多时候,用户的一些操作会增删某个子元素,如果一开始给每个子元素绑定事件,那么在子元素发生变化是,就需要给新增元素绑定事件,给要删除的元素解绑事件,使用事件委托就会省去很多类似的麻烦。

事件委托的实现:

<div id='container'>
 <button>按钮1</button>
 <button>按钮2</button>
 <button>按钮3</button>
 <button>按钮4</button>
</div>

<script>
 container.addEventListener('click', function(e){
     
     //把目标元素赋值给t
     let t = e.target 
     
     //判断是否匹配目标元素
     if (t.matchs('button')) {
         console.log('这是:' + t.textContent);
     }
 };
</script>
复制代码

这样点击 button 时会触发事件,打印出相应元素的文本内容。

基于此,可以封装一个事件委托函数

on('click', '#container', 'button', ()=>{
    console.log('这是:' + t.textContent)
})

function on(eventType, element, selector, fn) {
    //判断是不是 Element 
    //如果传进来的是选择器,不是 Element 本身,就先变成 Element
    //因为只有 Element 才能监听事件
    if (!(element instanceof Element)) {
        element = element.querySelector(element)
    }
    parentElement.addEventListener(eventType, (e)=>{
        let target = e.target
        if (target.matches(selector)) {
            fn(e)
        }
    })
}
复制代码

当被点击的元素有多个层级时,则需要递归判断其祖先元素,直到找到监听的元素(注意限定判断范围)

on('click', '#contaniner', 'button', ()=>{
    console.log('用户点击了li')
})

function on(eventType, element, selector, fn) {
    if (!(element instanceof Element)) {
        element = document.querySelector(element)
    }
 
    element.addEventListener(eventType, (e)=>{
        let el = e.target
        
        // 如果匹配到了selector就跳出循环
        while(!el.matches(selector)){
            if (el === element){
                
                //已经找到了父元素,说明还没找到,就设置为null
                el = null
                break
            }
            el = el.parentNode
        }
      
      	// 找到了el, 就调用函数
        el && fn.call(el, e, el)        
    })
}
复制代码
分类:
前端
分类:
前端