事件

作者: kiterumer | 来源:发表于2019-04-18 00:22 被阅读0次

    DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象,比如XMLHttpRequest,也部署了这个接口。
    该接口主要提供三个实例方法。

    • addEventListener: 绑定事件
    • removeEventListener: 移除事件
    • dispatchEvent: 触发事件

    addEventListener为例

    btn.addEventListener('click',function(){console.log(this)},false)
    

    第三个参数默认为false,可省略,指该事件只在冒泡阶段触发;若为true,则为在捕获阶段。
    addEventListener方法可以为针对当前对象的同一个事件,添加多个不同的监听函数。这些函数按照添加顺序触发,即先添加先触发。
    用队列理解,先进先出。

    • 进:addEventListener
    • 出:removeEventListener

    如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener方法手动去除)。

    注意,removeEventListener方法移除的监听函数,必须是addEventListener方法添加的那个监听函数,而且必须在同一个元素节点,否则无效。

    div.addEventListener('click', function (e) {}, false);
    div.removeEventListener('click', function (e) {}, false);
    

    上面代码中,removeEventListener方法无效,因为监听函数不是同一个匿名函数。

    监听函数

    浏览器的事件模型,就是通过监听函数对事件做出反应。事件发生后,浏览器听到了这个事件,就会执行相应的监听函数。这就是事件驱动编程模式的主要编程方式。
    JavaScript 有三种方法,可以为事件绑定监听函数。

    1. addEventListener (推荐这种)

    所有 DOM 节点实例都有addEventListener方法,用来为该节点定义事件的监听函数。

    window.addEventListener('load', doSomething, false);
    

    相比另外两种有如下优点:

    • 同一个事件可以添加多个监听函数。
    • 能够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数。
    • 除了 DOM 节点,其他对象(比如window、XMLHttpRequest等)也有这个接口,它等于是整个 JavaScript 统一的监听函数接口。

    2. 元素节点的事件属性

    元素节点对象的事件属性,同样可以指定监听函数。

    window.onload = doSomething;
    
    div.onclick = function (event) {
      console.log('触发事件');
    };
    

    使用这个方法指定的监听函数,也是只会在冒泡阶段触发。

    注意,这种方法与 HTML 的on-属性的差异是,它的值是函数名doSomething,而不像后者,必须给出完整的监听代码doSomething()

    同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。因此,也不推荐使用。

    3. HTML 的 on- 属性(不推荐)

    <body onload="doSomething()">
    <div onclick="console.log('触发事件')">
    

    上面代码为body节点的load事件、div节点的click事件,指定了监听代码。一旦事件发生,就会执行这段代码。

    注意,这些属性的值是将会执行的代码,而不是一个函数。

    一旦指定的事件发生,on-属性的值是原样传入 JavaScript 引擎执行。因此如果要执行函数,不要忘记加上一对圆括号。

    对象this、currentTarget和target

    在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget和target包含相同的值。

    1 var btn = document.getElementById("myBtn");
    2 btn.onclick = function (event) {
    3     alert(event.currentTarget === this); //ture
    4     alert(event.target === this); //ture
    5 };
    

    这个例子检测了currentTarget和target与this的值。由于click事件的目标是按钮,一次这三个值是相等的。如果事件处理程序存在于按钮的父节点中,那么这些值是不相同的。

    1 document.body.onclick = function (event) {
    2     alert(event.currentTarget === document.body); //ture
    3     alert(this === document.body); //ture
    4     alert(event.target === document.getElementById("myBtn")); //ture
    5 };
    

    当单击这个例子中的按钮时,this和currentTarget都等于document.body,因为事件处理程序是注册到这个元素的。然而,target元素却等于按钮元素,以为它是click事件真正的目标。由于按钮上并没有注册事件处理程序,结果click事件就冒泡到了document.body,在那里事件才得到了处理。

    currentTarget始终是监听事件者,而target是事件的真正发出者。

    注意,在jQuery提供的on方法中,e.currentTarget与该方法接收的第二个参数有关,根据jQuery的文档描述

    如果省略selector或者是null,那么事件处理程序被称为直接事件 或者 直接绑定事件 。每次选中的元素触发事件时,就会执行处理程序,不管它直接绑定在元素上,还是从后代(内部)元素冒泡到该元素的
    当提供selector参数时,事件处理程序是指为委派事件(事件委托或事件代理)。事件不会在直接绑定的元素上触发,但当selector参数选择器匹配到后代(内部元素)的时候,事件处理函数才会被触发。jQuery 会从 event target 开始向上层元素(例如,由最内层元素到最外层元素)开始冒泡,并且在传播路径上所有绑定了相同事件的元素若满足匹配的选择器,那么这些元素上的事件也会被触发。

    <!DOCTYPE html>
    <html>
    <head>
      <script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
      <meta charset="utf-8">
      <title>JS Bin</title>
      <style>
        li{
          padding: 5px;
          border: 1px solid red;
        }
        span{
          border: 1px solid #000;
        }
      </style>
    </head>
    <body>
      <ul>
        <li><span>hello 1</span></li>
        <li><span>hello 1</span></li>
        <li><span>hello 1</span></li>
        <li><span>hello 1</span></li>
      </ul>
      <script>
        let ul = document.querySelectorAll('ul')[0]
        let aLi = document.querySelectorAll('li')
        $('ul').on('click','li',function(e){
     
            console.log(e.target)   //  被点击的元素
            console.log(e.currentTarget)   //  li   
            console.log(e.currentTarget === this)  // true
        })
      </script>
    </body>
    </html>
    

    当li中含有子元素的时候,e.target指的是触发事件的元素,可能是span也可能是li,此时的e.currentTarget指的是selector那个参数,也就是本例中的li。如果省略selector参数,那么它和addEventListener中e.target和e.currentTarget是一致的。

    事件委托

    事件委托就是利用事件冒泡机制,指定一个事件处理程序,来管理某一类型的所有事件。
    举个取快递的例子:
    公司的员工们经常会收到快递。为了方便签收快递,有两种办法:一种是快递到了之后收件人各自去拿快递;另一种是委托前台MM代为签收,前台MM收到快递后会按照要求进行签收。
    很显然,第二种方案更为方便高效,同时这种方案还有一种优势,那就是即使有新员工入职,前台的MM都可以代替新员工签收快递。
    这个例子之所以非常恰当形象,是因为这个例子包含了委托的两层意思:首先,现在公司里的员工可以委托前台MM代为签收快递,即程序中现有的dom节点是有事件的并可以进行事件委托;其次,新入职的新员工也可以让前台MM代为签收快递,即程序中新添加的dom节点也是有事件的,并且也能委托处理事件。

    如果一个ul中有100li,每个li都需要处理click事件,我们可以遍历所有li,给它们添加事件处理程序,这并不是理想的解决方案。
    事件委托怎么实现呢?因为冒泡机制,既然点击子元素时,也会触发父元素的点击事件。那么我们就可以把点击子元素的事件要做的事情,交给最外层的父元素来做,让事件冒泡到最外层的dom节点上触发事件处理程序,这就是事件委托。

    相关文章

      网友评论

          本文标题:事件

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