美文网首页
事件传播机制和事件代理

事件传播机制和事件代理

作者: 江平路 | 来源:发表于2020-08-28 15:06 被阅读0次

    事件传播机制

    一次事件的发生包含三个过程:

    (1)capturing phase:事件捕获阶段。
    事件被从document一直向下传播到目标元素,在这过程中依次检查经过的节点是否注册了该事件的监听函数,若有则执行。

    (2)target phase:事件处理阶段。
    事件到达目标元素,执行目标元素的事件处理函数.

    (3)bubbling phase:事件冒泡阶段。
    事件从目标元素上升一直到达document,同样依次检查经过的节点是否注册了该事件的监听函数,有则执行。

    document.onclick = function(){}
    document.documentElement.onclick = function(){}
    document.body.onclick = function(){}
    outer.onclick = funciton(){}
    inner.onclick = funciton(){}
    
    image.png

    1. 是什么?

    <div id="outer">
        <p id="inner">Click me!</p>
    </div>
    
    

    上面的代码当中一个div元素当中有一个p子元素,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数会首先被触发呢?

    为了解决这个问题微软和网景提出了两种几乎完全相反的概念。

    **事件流 : ** 指的是页面中接收事件的顺序.

    事件冒泡

    微软提出了名为事件冒泡(event bubbling)的事件流。
    事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。

    当一个DOM元素上的事件被触发的时候(如:按钮点击事件),这个元素的所有父元素 中,如果也绑定有该相同事件,则也会被触发, 触发的顺序就是先从 : 当前元素的事件 ==> 临近父元素 ==> 父元素......,这一过程被称为事件冒泡

    因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是:
    p -> div -> body -> html -> document
    IE,火狐和chrome浏览器都是事件冒泡.

    事件捕获

    网景提出另一种事件流名为事件捕获(event capturing)。
    当一个DOM元素上的事件被触发的时候(如:按钮点击事件),这个元素的所有父元素 中,如果也绑定有该相同事件,则也会被触发, 触发的顺序就是先从 : ....... 父元素 ==> 临近父元素 ==> 当前元素的事件,这一过程被称为事件捕获
    与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
    上面的例子在事件捕获的概念下发生click事件的顺序应该是:
    document -> html -> body -> div -> p

    图解:

    image

    2. 解决了什么问题?

    这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。

    http://www.imooc.com/article/9833

    3. 执行原理

    <ul>
       <li>
           <p>
              <a>  </div>
          </p>
       </li>
    </ul>
    
    
    image

    事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)。
    事件冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签。

    事件捕获当你使用事件捕获时,父级元素先触发,子级元素后触发,即div先触发,p后触发。
    事件冒泡当你使用事件冒泡时,子级元素先触发,父级元素后触发,即p先触发,div后触发。

    4. 如何使用?

    说到事件的执行顺序,那么我们需要知道一个给元素添加事件的方法, 即给某元素动态绑定事件
    W3C为我们提供了addEventListener()函数用来为指定的dom元素动态绑定事件。

    语法:
    element.addEventListener( event ,  function ,  useCapture )
    
    
    image

    提示: 使用 removeEventListener()方法来移除 addEventListener() 方法添加的事件句柄。

    function sayHello() { 
    console.log("hello");
    }
    var myDiv = document.getElementById("myDiv");
    myDiv.addEventListener("click", sayHello);
    
    这样我们点击id为myDiv的元素时,控制台就会输出"Hello"。
    
    
    事件冒泡

    有如下html代码:

    image image

    下面设置了四个函数用来进行事件绑定:

    image

    使用下面的代码,我们可以获取四个元素对应DOM

    image

    现在,我试着同时分别为grandpa和grandson绑定sleep和doingHomework事件:

    image

    这时我们点击最外层的grandpa时,当然会触发sleep函数,然而当我们点击grandson时,控制台的输出如下:

    image

    原因:这是因为grandson在grandpa之上,当点击grandson时,同时也在grandpa上进行了点击操作,所以在执行了doingHomework后,还会触发grandpa的sleep函数。
    这种当满足条件后从子元素到父元素依次触发其上事件的处理方式叫做事件冒泡

    我们也为father和child分别绑定watchTV和playingCard函数

    image image
    事件冒泡()
    grandpa.addEventListener("click", sleep);            
    grandson.addEventListener("click", doingHomework);            
    father.addEventListener("click", watchTV);            
    child.addEventListener("click", playingCard);
    
    
    image image

    事件捕获

    事件捕获与事件冒泡完全相反,先触发祖先元素的事件,然后再逐级触发子元素的事件。默认情况下,绑定事件时,采用事件冒泡原则,如果想要进行事件捕获的话,需要设置一个参数 。

    可以为addEventListener函数添加第三个参数useCapture,参数值是布尔值,默认是false。当useCapture为false时,事件处理采取事件冒泡的原则,当userCapture为true时,则采取事件捕获的原则

    image

    这时,当点击grandson时,就会先执行祖先元素的事件,再执行后代元素的事件了,控制台的输出如下图所示:

    image

    虽然默认情况下,useCapture的值是false,但我推荐我们在绑定函数时把它明显的写出来以避免浏览器兼容性的问题。

    事件冒泡与事件捕获要是同时进行怎么办

    有思想的同学肯定会思考这样一个问题,在上述绑定事件的代码中,第三个参数不是全部设置的true,就是全部设置成false,那如果既有true,又有false,有的元素设置成按事件冒泡处理,有的元素设置成按事件捕获处理,那怎么办呢?
    直接告诉大家答案,我们的浏览器更“喜爱”事件捕获:

    它会先把useCapture为false的元素绑定事件放到一边,按照事件捕获正常的顺序执行useCapture为true的元素绑定事件,最后在按照事件冒泡顺序执行useCapture为false。
    现在我们作如下更改

    image

    按照上述原则,当点击grandson时,先执行useCapture为true的元素的绑定事件,又按照事件捕获原则,先执行grandpa的事件,再执行child的事件。之后,再按照事件捕获顺序执行useCapture为false的事件,输出结果如下:

    image

    阻止事件冒泡和捕获

    我们可以利用时间对象event的stopPropagation()方法阻止事件的进一步传播。
    我们修改一下doingHomework函数:

    image image

    发现事件执行到doingHomework就被阻断了,其后不会在事件传播到父元素。
    值得注意的是,event.stopPropagation()函数并不会阻止其下函数内容的执行。

    *如果你使用的是jquery的事件绑定,也可以直接在函数中使用return false来阻止事件的传播(当然event.stopPropagation同样有效),与event.stopPropagation不同的是,return false会阻止同函数下面的代码执行 *

    传统绑定事件方式在一个支持W3C DOM的浏览器中,像这样一般的绑定事件方式,是采用的事件冒泡方式。
    ele.onclick = doSomething2

    IE浏览器
    如上面所说,IE只支持事件冒泡,不支持事件捕获,它也不支持addEventListener函数,不会用第三个参数来表示是冒泡还是捕获,它提供了另一个函数attachEvent。
    ele.attachEvent("onclick", doSomething2);

    不是所有的事件都能冒泡,例如:blur、focus、load、unload。

    事件委托

    利用事件的冒泡传播机制,如果一个容器的后代元素中,很多元素的点击行为(其他行为也是)都要做一些处理,此时我们不需要再像以前一样一个一个获取绑定了,我们只需要给容器的CLICK绑定方法即可,这样不管点击哪一个后代元素,就会根据冒泡的传递机制,把容器的CLICK行为触发,把对应的方法执行,根据事件源,我们可以知道点击的是谁,从而做不同的事情即可。

    image

    参考链接:https://www.jianshu.com/p/bd6e6bb968e0

    相关文章

      网友评论

          本文标题:事件传播机制和事件代理

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