javascript事件委托(事件代理)

1.原理:

事件委托就是利用事件冒泡机制指定一个事件处理程序,来管理某一类型的所有事件。

即:利用冒泡的原理,把事件加到父级上,触发执行效果。

好处:

  • 只在内存中开辟了一块空间,节省资源同时减少了DOM操作,提供性能
  • 对于新添加的元素也会有之前的事件原

事件委托原理我们理解了,现在我们先来理解一下事件流,事件流有事件捕获和事件冒泡的阶段,其实也可以理解为三个阶段,事件捕获-->目标对象事件(target)-->事件冒泡阶段, 用下面这张图来理解清晰明了(网上的图):

1.事件捕获,当一个事件触发后,从window对象触发,不断经过下级节点。在事件到达目标节点之前的过程就是捕获阶段。所有经过的节点都会触发对应的事件。

2.事件冒泡,当事件到达目标节点后,会沿着捕获阶段的路线,原路返回。同样,所有经过的节点,都会触发对应的事件。

W3C标准中的事件添加方法:addEventListener(type,fn,useCapture);中的第三个参数就是指事件是在哪个阶段触发,可选参数,默认为false。true:事件句柄在捕获阶段执行;false:事件句柄在冒泡阶段执行。

2.JQ的事件委托实现

我们首先来看一看jQuery的事件委托是怎么做的。

jq有个on添加事件的方法,也可以做事件委托。使用 on() 方法添加的事件处理程序适用于当前及未来的元素(比如由脚本创建的新元素)。

使用:

$(selector).on(event,childSelector,data,function) 

参数 说明:

参数描述
event必需。规定要从被选元素移除的一个或多个事件或命名空间。

由空格分隔多个事件值,也可以是数组。必须是有效的事件。
childSelector可选。规定只能添加到指定的子元素上的事件处理程序(且不是选择器本身,比如已废弃的 delegate() 方法)。
data可选。规定传递到函数的额外数据。
function可选。规定当事件发生时运行的函数。

重点是上面的参数可以指定为该事件使用的事件代理,就是代理子元素事件的父级节点。childSelector就是目标元素,即点击了这个childSelector元素,触发事件,但是什么时候处理执行事件方法呢,是事件冒泡到selector节点的时候执行事件回调函数。

举个栗子:

$('body').on('click','.btns',function(){
    //do something
    console.log(123);
})

 上面的栗子是说,我页面上所有现有的或者动态新添加的含有btn类的元素的点击事件,都委托给body元素处理,即点击含有btn类的元素要到事件捕获再冒泡到body元素上才执行事件回调。

3.原生JS的实现 

1.我们可以用原生的JS封装一个事件绑定并具有事件委托功能的函数。如下:

function bindEvent(elem,type,selector,fn){
    //当只有三个参数时我们一般第三个参数是fn,则没有事件委托机制,需要把fn赋值为第三个参数
    if(!fn){
        fn = selector;
        selector = null;
    }
    elem.addEventListener(type,function(ev){
        if(selector){
            //如果使用了事件委托,需要匹配到目标元素
            let target = ev.target;
            if(target.matches(selector)){
                fn.call(target,ev);
            }
        }else{
            //普通绑定事件直接调用fn
            fn(ev);
        }
    })
}

target 和currentTarget的区别:

  • target在事件流的目标阶段,一个触发事件的对象的引用。
  • currentTarget当事件遍历DOM时,标识事件的当前目标。它总是引用事件处理程序附加到的元素,而不是event.target,它标识事件发生的元素。

只有当事件流处于目标阶段的时候,他们两个的指向才是一致的,而处于捕获和冒泡阶段的时候,target指向被单击的对象,而currentTarget指向当前事件活动的对象(注册该事件的对象,一般为父级),简单来说:currentTarget指的是事件触发后,冒泡到绑定处理程序的元素,就是绑定事件处理程序的元素,target指的是触发事件的元素。

 

另外,我们在获取目标元素的时候用到了Element.matches方法

语法:

let result = element.matches(selectorString);
  • result值为true或者false
  • selectorString是个css选择器

2.当然也可以用target.nodeName.toLowerCase() === 'button'来进行获取目标触发元素,以下作为例子结构:

<body>
    <div class="box">
        <div class="box2">
            <div class="box3">
                <button id="btn"><a>button</a></button>
            </div>
        </div>
    </div>
    <script>
        let elem = document.querySelector('.box');
        elem.addEventListener('click',function(ev){
            let target = ev.target;
            while(!target.classList.contains('box')){
                if(target.nodeName.toLowerCase()==='button'){
                    //do something
    
                    break;
                }
                target = target.parentNode;
            }
        })
    </script>
</body>

当事件没有冒泡到目标元素的时候就一直循环到找到目标节点,否则什么也不做。

这里我们对比上面原生JS实现事件委托的两种方式的区别,除了匹配元素使用的 matches 方法不一样,我们第二种方式还用到了 while 循环去寻找目标对象,对于匹配使用的无论是 nodeName 还是 matches 并没有多大区别只是匹配目标元素(IE不支持matches 方法)。

事件委托用不用 while 循环去寻找取决于你的 html 结构,如果你的目标元素就是最底层(即点击的时候就是 ev.target 指向的元素)那么就不需要使用 while 循环查找。例如以下结构你的目标元素是 li,li 里面不再有元素节点。

<div>
    <ul>
        <li>我是li1</li>
        <li>我是li2</li>
        <li>我是li3</li>
    </ul>
</div>

另一种,如果你的目标元素中还有元素节点,但是你的目标仍然是 li,这个时候就需要使用 while + parentNode 去查找目标节点:

<div>
    <ul>
        <li><span>我是li1</span></li>
        <li><span>我是li2</span></li>
        <li><span>我是li3</span></li>
    </ul>
</div>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值