• 前端面试必考:事件委托


    先看几道面试题

    1. 描述下js里面的事件流
    2. 默认情况下,事件是在冒泡阶段执行还是捕获阶段执行
    3. 请简要说明事件委托原理和使用场景
    4. 手写原生js实现事件代理,注意浏览器兼容

    如果上面的面试题,您不能很顺利的作答,那么希望这篇文件对您能有一些帮助。如果出现错误,请您及时指正,谢谢。

    什么是事件委托

    事件委托也叫事件代理,《Javascript高级程序设计》中写道:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
    想要理解事件委托,需要先理解js事件流。


    js事件流

    图为事件流的全过程,从图中我们可以看出:

    • 一个完整的事件流是从window开始,最后回到window的一个过程
    • 事件流被分为3个阶段:1-5捕获阶段,5-6:目标阶段,6-10:冒泡阶段

    观察代码,并说出打印结果
    html

    <body>
        <div style="height: 100px;background: grey;"></div>
    </body>
    

    js

    var oDiv = document.querySelector('#div');
    oDiv.addEventListener('click',function(){
        console.log('div')
    });
    document.body.addEventListener('click',function(){
        console.log('body')
    });
    
    var oDiv = document.querySelector('#div');
    oDiv.addEventListener('click',function(){
        console.log('div')
    },false);
    document.body.addEventListener('click',function(){
        console.log('body')
    },false);
    
    var oDiv = document.querySelector('#div');
    oDiv.addEventListener('click',function(){
        console.log('div')
    },true);
    document.body.addEventListener('click',function(){
        console.log('body')
    },true);
    

    通过上述代码,我们发现:

    • addEventListener有3个参数:event, function, useCapture(可选)
    • useCapture为true时,事件在捕获阶段执行,打印结果为body、div
    • useCapture为false时,事件在冒泡阶段执行,打印结果为div、body
    • useCapture值不传时,事件在冒泡阶段执行,打印结果为div、body

    阻止默认冒泡行为,我们可以用

    ev.stopPropagation()
    

    现在,我们认识了js事件流,接下来,说一说事件委托。


    js事件委托

    我们先来看一个场景:

    现在有一个todo list,需要实现以下功能:

    1. 点击‘添加’按钮时,列表中新增item,内容为文本框输入内容
    2. todo Item 点击'完成'按钮时自动删除

    我们知道的给DOM绑定事件的方法有以下几种:

    嵌入DOM

    例如:

    <li>想你 <button onclick="complete">完成</button></li>
    
    直接绑定
    oBtn.onclick = complete
    
    事件监听
    oBtn.addEventListener('click',complete)
    

    需求很简单,但是有两点需要注意:

    • 如果给‘完成’按钮绑定事件,因为不断有新DOM的生成,每次生成都要重新绑定,代码不便管理
    • 如果给‘完成’按钮绑定事件,则需要循环遍历每一个‘完成’按钮,不断访问DOM,按钮越多,访问次数越多,性能越低

    为了解决上述两个问题,‘事件代理’便是完美的解决方案。

    实现思路
    将事件绑定到父元素ul上,当用户点击按钮时,通过事件流,冒泡到父元素ul,从而执行回调。

    事件代理的好处

    • 只绑定一次事件,无频繁访问DOM,性能较高
    • 当有新DOM生成时,无需重复绑定事件,代码清晰简洁

    现在,我们使用事件代理的方式实现上述需求,可是我应该如何判断点击的是‘完成’按钮,而不是其他元素呢?


    事件对象

    事件对象是在事件发生时产生,用来记录事件发生时的一些信息。

    oUl.addEventListener('click',function(ev){
        var ev = ev || event;
        console.log(ev);
    })
    

    事件对象中记录了点击事件发生时的目标元素,但由于浏览器存在差异,所以兼容性的写法为:

       var origin = ev.target || ev.srcElement
    

    手写原生js实现事件代理,浏览器兼容

    function bindEvent(obj, type, fn) {
        if (obj.addEventListener) {
          obj.addEventListener(type, eventFn);
        } else {
          obj.attachEvent("on" + type, eventFn);
        }
    
        function eventFn(ev) {
          var ev = ev || window.event;
          var target = ev.target || ev.srcElement;
          fn && fn(target, ev)
        }
      }
    
  • 相关阅读:
    UML_状态图
    UML_时序图
    UML_类图
    浅谈依赖注入
    MyEclipse_搭建SSH框架
    AOP:面向切面编程
    工厂模式
    (转)oracle使用expdp、impdp和exp、imp导入导出表及表结构
    oracle exp 和 imp 数据和表结构互相独立导出导入
    oracle 清空当前用户所有对象
  • 原文地址:https://www.cnblogs.com/yxqd/p/10369392.html
Copyright © 2020-2023  润新知