美文网首页
事件冒泡、事件捕获、事件代理

事件冒泡、事件捕获、事件代理

作者: 不退则进_笑 | 来源:发表于2020-10-22 14:26 被阅读0次

    1,背景

    • 事件冒泡是微软提出
    • 事件捕获是网景公司提出
    • 都是为了解决页面中事件发生的顺序,但是他们的提出几乎相当于 2 种完全相反的概念

    2,例子:

    <div id="outer" onClick={()=>{}}>
        <p id="inner" onClick={()=>{}}>Click me!</p>
    </div>
    

    3,事件冒泡

    • 事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到 document 对象

    • 因此在事件冒泡的概念下在 p 元素上发生 click 事件的顺序应该是 p -> div -> body -> html -> document

    4,事件捕获

    • 网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素
    • 因此在事件捕获的概念下在 p 元素上发生 click 事件的顺序应该是 document -> html -> body -> div -> p

    5,addEventListener 的第三个参数

    • 网景 和 微软 曾经的战争还是比较火热的,当时, 网景主张捕获方式,微软主张冒泡方式。后来 w3c 采用折中的方式,平息了战火,制定了统一的标准——先捕获再冒泡
    • addEventListener 的第三个参数就是为冒泡和捕获准备的
    • element.addEventListener(event, function, useCapture)
    • 第一个参数是需要绑定的事件
    • 第二个参数是触发事件后要执行的函数
    • 第三个参数默认值是 false,表示在事件冒泡阶段调用事件处理函数;如果参数为 true,则表示在事件捕获阶段调用处理函数。

    6,冒泡的案例

    <div id="s1">s1
        <div id="s2">s2</div>
    </div>
    <script>
        s1.addEventListener("click",function(e){
            console.log("s1 冒泡事件");
        },false);
        s2.addEventListener("click",function(e){
            console.log("s2 冒泡事件");
        },false);
    </script>
    
    // s2 冒泡事件
    // s1 冒泡事件
    

    7,捕获的案例

    <div id="s1">s1
        <div id="s2">s2</div>
    </div>
    <script>
        s1.addEventListener("click",function(e){
            console.log("s1 捕获事件");
        },true);
        s2.addEventListener("click",function(e){
            console.log("s2 捕获事件");
        },true);
    </script>
    
    // s1 捕获事件
    // s2 捕获事件
    

    8,事件捕获和事件冒泡,一起存在

    <div id="s1">s1
        <div id="s2">s2</div>
    </div>
    <script>
    s1.addEventListener("click",function(e){
            console.log("s1 冒泡事件");
    },false);
    s2.addEventListener("click",function(e){
            console.log("s2 冒泡事件");
    },false);
    
    s1.addEventListener("click",function(e){
            console.log("s1 捕获事件");
    },true);
    
    s2.addEventListener("click",function(e){
            console.log("s2 捕获事件");
    },true);
    </script>
    
    
    • s1 捕获事件
    • s2 冒泡事件
    • s2 捕获事件
    • s1 冒泡事件
    8.1 执行顺序
    • document 往 target 节点,捕获前进,遇到注册的捕获事件立即触发执行
    • 到达 target 节点,触发事件(对于 target 节点上,是先捕获还是先冒泡取决于捕获事件和冒泡事件的注册顺序,先注册先执行)
    • target 节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发
    8.2 总结
    • 对于非 target 节点则先执行捕获在执行冒泡
    • 对于 target 节点则是先执行先注册的事件

    8,选择冒泡还是捕获?

    • 对于事件代理来说,在事件捕获或者事件冒泡阶段处理并没有明显的优劣之分,但是由于事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型

    9,IE 浏览器兼容

    • IE 浏览器对 addEventListener 兼容性并不算太好,只有 IE9 以上可以使用
    • 要兼容旧版本的 IE 浏览器,可以使用 IE 的 attachEvent 函数
    • object.attachEvent(event, function)
    • 两个参数与 addEventListener 相似,分别是事件和处理函数,默认是事件冒泡阶段调用处理函数,要注意的是,写事件名时候要加上"on"前缀("onload"、"onclick")等

    10,事件代理(事件委托)

    • 事件委托的原理是 DOM 元素的事件冒泡,把里层所需要响应的事件绑定到外层

    • 使用“事件委托”时,并不是说把事件委托给的元素越靠近顶层就越好。事件冒泡的过程也需要耗时,越靠近顶层,事件的”事件传播链”越长,也就越耗时。如果 DOM 嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失。

    • 例子:

    <ul id="myLinks">
          <li id="goSomewhere">Go somewhere</li>
          <li id="doSomething">Do something</li>
          <li id="sayHi">Say hi</li>
        </ul>
        <script>
          var item1 = document.getElementById('goSomewhere')
          var item2 = document.getElementById('doSomething')
          var item3 = document.getElementById('sayHi')
    
          item1.onclick = function () {
            location.href = 'http://www.baidu.com'
          }
          item2.onclick = function () {
            document.title = '事件委托'
          }
          item3.onclick = function () {
            alert('hi')
          }
        </script>
    

    如果在一个复杂的 Web 应用程序中,对所有可单击的元素都采用这种方式,那么结果就会有数不 清的代码用于添加事件处理程序。此时,可以利用事件委托技术解决这个问题。使用事件委托,只需在 DOM 树中尽量最高的层次上添加一个事件处理程序,

    • 如下面的例子所示:
     <script>
          var allItems = document.getElementById('myLinks')
          allItems.addEventListener('click', function (event) {
            var target = event.target
            switch (target.id) {
              case 'doSomething':
                document.title = '事件委托'
                break
              case 'goSomewhere':
                location.href = 'http://www.baidu.com'
                break
              case 'sayHi':
                alert('hi')
                break
            }
          })
        </script>
    

    相关文章

      网友评论

          本文标题:事件冒泡、事件捕获、事件代理

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