美文网首页
JS事件流(冒泡、捕获)addEventListener详解

JS事件流(冒泡、捕获)addEventListener详解

作者: 匿于烟火中 | 来源:发表于2020-03-01 22:09 被阅读0次

    前置知识:DOM结构
    JS和HTML之间通过事件来进行交互。当文档或者浏览器窗口发生了特殊的交互,就是用监听器来预定事件,以便事件发生时执行响应的代码(观察者模式)。

    事件流


    随着浏览器的发展,浏览器开发团队遇到了一个问题:页面的哪一个部分会拥有某个特定的事件?举例来说,比如纸上有一组同心圆。把手指在同心圆上,那么手指指向的不止是一个圆,而是纸上的所有圆。所以后来浏览器开发团队认为,当你单击了某个按钮,除了单击了这个按钮,也单击了它的容器元素和整个页面。
    事件流描述的是从页面中接收事件的顺序。
    事件流有两种模式:分别是事件冒泡和事件捕获。

    • 以下图结构为例来解释事件冒泡和事件捕获
    • 布局图


      示例图.PNG
    • 当我们点击了line-two-center-child这个块元素时。DOM树当中发生的事件流顺序如下图。
      DOM事件流.PNG

    事件捕获(event capture)


    不太具体的node应该更早接收到事件,而最具体的node应该最后接收到事件。事件捕获的目的在于在事件到达预定目标之前捕获它。
    在上图的布局中,绿色箭头代表的就是事件捕获发生时,DOM树中的node接收到事件的先后顺序。可以看到它是由最外层的document一层一层向内部传递事件。

    事件冒泡(event bubble)


    即为事件开始时从最具体的元素慢慢接收,然后逐级向上传播到较为不具体的节点。即为由最深的node向最外围的node扩散。
    在上图布局中,红色箭头代表的就是事件捕获发生时,DOM树中的node接收到事件的先后顺序。可以看到它是由最内层的center-child一层一层向内部传递事件。

    DOM事件流


    “DOM标准事件流”:包含三个阶段事件捕获阶段、处于目标阶段、事件冒泡阶段。先后顺序是,事件捕获(可以截取事件)=>实际目标接收到事件=>冒泡阶段(可以对这个事件作出响应)

    在捕获阶段,捕获不会到达具体的目标节点,到达目标节点处理事件被看作是冒泡阶段的一部分。

    DOM0级事件

    直接指定每个元素上面的事件处理属性。DOM0级方法指定的事件处理方法认为是元素的方法。因此,这个时候事件处理程序是在元素的作用域中运行的。this指向引用当前元素。

    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
       console.log(this.id);//myBtn
    }
    

    DOM0级事件会在事件流的冒泡阶段被处理。
    删除0级事件btn.onclick = null

    DOM2级事件

    addEventListener('eventName',callback,ifbubble):三个参数分别是事件名字符串,回调函数,布尔值。第三个参数为true,表示在捕获阶段调用事件处理程序。为false,表示在冒泡阶段调用事件处理。默认是使用冒泡。
    removeEventListener():移除addEventListener添加的事件

    2级事件和0级事件的区别:2级事件可以添加多个事件处理程序,处理顺序会按照它们的添加顺序触发,而0级事件只有最后一次添加能生效。

    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
       console.log(1);
    }
    btn.onclick = function(){
       console.log(2);
    }
    //btn点击后输出2
    
    var btn = document.getElementById("myBtn");
    btn.addEventListener('click', function(){
       console.log(1);
    },false)
    btn.addEventListener('click', function(){
       console.log(2);
    },false)
    // btn点击后先输出1再输出2
    
    • 2级事件的问题:如果addEventListener()添加的回调是匿名函数,会无法通过removeEventListener()移除。
    btn.addEventListener('click', function(){
       console.log(2);
    },false)
    btn.removeEventListener('click', function(){
       console.log(2);
    },false);//无法删除
    

    因为传入的匿名函数会认为是一个全新的函数。

    事件对象对象(event)

    在触发DOM上的某个事件的时候,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。

    btn.addEventListener('click', function(event){
       console.log(event.type);//"click"
    },false);
    
    <input type="button" value="click me" oclick="alert(event.type)">
    

    event对象的主要成员

    属性方法 类型 说明
    currentTarget Element 其事件处理程序当前正在处理的那个元素,比如,因为子node元素的方法触发了,冒泡到父node的的时候,currentTarget指向父node
    target Element 事件的目标节点,也就是target一般是不变的,在哪个node触发就是哪个node
    type String 被触发的事件类型
    bubbles Boolean 表明事件是否冒泡
    cancelable Boolean 表明是否可以取消事件的默认行文
    defaultPrevented Boolean 为true表明已经调用了preventDefault()
    eventPhase Interger 调用事件处理的阶段,1为捕获,2为处于目标,3为冒泡
    preventDefault() Function 取消事件的默认行为
    stopPropagation() Function 取消事件的进一步捕获或冒泡

    在事件回调方法内部,this始终等于currentTarget。只有在事件处理程序执行期间,event对象才会存在;一旦事件狐狸程序执行完成,event对象就会被销毁。

    事件委托


    在js当中,添加到页面上的eventhandler的数量会直接关系到页面的整体运行性能。因为,每个函数都是对象,会占用内存,内存中对象越多性能就越差,其次,必须事先指定所有eventhandler而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
    对于eventhandler过多的问题解决方案就是事件委托

    事件委托:利用了事件冒泡,只指定一个eventhandler就可以管理某一类型的所有事件。

    <ul id="list">
      <li id='1'></li>
      <li id='2'></li>
      <li id='3'></li>
    </ul>
    

    如果想要给3个li添加事件监听,从事件委托的角度来看,只需要给父元素ul添加监听器,那么3格li上的事件触发的时候,都会冒泡到ul。通过检测target对象(target就是触发事件的最底层对象)上的id可以判断具体是哪个li触发的操作。

    移除EventHandler

    当元素绑定eventhandler时,运行中的浏览器代码与js之间会建立一个连接。连接越多,页面执行越慢。

    • 当通过纯粹的DOM操作,如removeChild()replaceChild(),或者在使用innerHTML替换页面中某一部分,绑定了eventhandler的元素被innerHTML删除了,会导致原来添加到元素中的eventhandler很有可能不被当作垃圾回收。
    <div id="myDiv">
      <input type="button" value="click me" id="myBtn"/>
    </div>
    
    <script>
       var btn = document.getElementById("myBtn");
       btn.onclick = function(){
         document.getElementById("myDiv").innerHTML = "process.."
    //myBtn绑定了事件,但是事件执行完被清除掉了,但是onclick绑定的事件还在
       }
    </script>
    
    • 在卸载页面的时候可能存在空事件处理程序。
    尽量使用事件委托可以减少事件绑定。

    相关文章

      网友评论

          本文标题:JS事件流(冒泡、捕获)addEventListener详解

          本文链接:https://www.haomeiwen.com/subject/dquufhtx.html