[TOC]

前端-捕获冒泡、代理、派发代码示例及封装

今天被实习生问到事件代理和派发的问题,举举例子讲了一番后,然后有了此文。

目录大纲

  1. 捕获冒泡示例
  2. 事件代理示例
  3. 事件派发示例
    1. 原理介绍:发布订阅者模式
    2. 事件派发,完整流程的封装(发布订阅者模式)

1. 捕获冒泡示例

事件监听api:addEventListener,有第三个参数,isCapture,意思是:是否捕获,默认false 冒泡

html部分

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
</head>
<body>
<div>
   <p id="parEle">
       我是父元素 
       <span id="sonEle">
           ---我是子元素---
       </span>
   </p>
</div>
</body>
</html>

script部分

  1. 测试例子1:(全冒泡,先子后父)(addEventListener第三个参数不写,默认冒泡) ```html

    点击“—-我是子元素—-”,打印: 子级111 子级 父父父父父父级111111111 父父父父父父级

    点击“我是父元素”,打印: 父父父父父父级111111111 父父父父父父级

    
    2. 测试例子2:(全捕获,先父后子)(addEventListener第三个参数写 true) 
    ```html
    <script type="text/javascript">
    var sonEle = document.getElementById('sonEle');
    var parEle = document.getElementById('parEle');
    
    // 测试例子2:(全捕获,先父后子)
    parEle.addEventListener('click', function () {
        console.log('父父父父父父级111111111');
    }, true);  
    sonEle.addEventListener('click', function () {
        console.log('子级111');
    }, true);
    parEle.addEventListener('click', function () {
      console.log('父父父父父父级');
    }, true);
    sonEle.addEventListener('click', function () {
      console.log('子级');
    }, true);
    </script>
    
    点击“---我是子元素---”,打印:
    父父父父父父级111111111
    父父父父父父级
    子级111
    子级
    
    点击“我是父元素”,打印:
    父父父父父父级111111111
    父父父父父父级
    
    1. 测试例子3:(前2个冒泡,后2个捕获) ```html

      点击“—-我是子元素—-”,打印: 父父父父父父级 子级 子级111 父父父父父父级111111111

      点击“我是父元素”,打印: 父父父父父父级 父父父父父父级111111111

      
      <a name="128d0d23"></a>
      ## 2. 事件代理示例
      
      好处:不用一个个绑定子元素事件了,直接绑父元素,然后用event.target来判断对应的子元素
      
      ```html
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>事件冒泡</title>
      </head>
      <body>
        //我们有一个div元素,它包含三个按钮:
        <div id="box">
          <input type="button" value="按钮">
          <input type="button" value="按钮2">
          <input type="button" value="按钮3">
        </div>
        <script>
          var box = document.getElementById('box');  
          // 好处: 不用一个个绑定子元素事件了
          box.addEventListener('click', function(event) {
            console.log(1)
            if (event.target.tagName.toLowerCase() === 'input') {
               // some code
              console.log(event.target)
            }
          });
        </script>
      </body>
      </html>
      

      3. 事件派发示例

      可以自定义事件new Event()

      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>派发事件</title>
      </head>
      <body>
        <div id="box">点我派发事件</div>
        <script>
          // 初始化事件 NBA 
          var eventNBA = new Event("NBA", {"bubbles":true, "cancelable":false});
          // 订阅者1 订阅NBA
          document.addEventListener('NBA', function (event) {
              console.log(7227)
              console.log(event)
          }, false);
          // 订阅者2 订阅NBA
          document.addEventListener('NBA', function (event) {
              console.log(234243223)
              console.log(event)
          }, false);
      
          document.getElementById('box').addEventListener('click', function(event) {
            document.dispatchEvent(eventNBA); // 事件派发:通知所有订阅了NBA的订阅者,触发他们的回调函数
          });
        </script>
      </body>
      </html>
      

      1. 原理介绍:发布订阅者模式

      可看我的另一篇: https://juejin.cn/post/6954250097651613733

      publish.png

      2. 事件派发,完整流程的封装(发布订阅者模式)

      class EventEmitter {
        constructor () {
          this._event = {}
        }
        on (dom, eventName, fn) { // 为dom绑定一个自定义event
          this._event[eventName] = new Event(eventName)
          dom.addEventListener(eventName, fn)
        }
        off (dom, eventName, fn) { // 为dom注销一个自定义event
          this._event[eventName] = null
          dom.removeEventListener(eventName, fn)
        }
        emit (dom, eventName) { // 事件派发
          if (!this._event[eventName]) throw '不存在' + eventName
          dom.dispatchEvent(this._event[eventName])
        }
        once (dom, eventName, fn) { // 事件派发一次 就注销
          this.emit(dom, eventName)
          this.off(dom, eventName, fn)
        }
      }
      
      var myEmit = new EventEmitter()
      var fn = function (e) {
        console.log(e)
      }
      myEmit.on(document, 'okok', fn)
      myEmit.off(document, 'okok', fn)
      myEmit.once(document, 'okok', fn)
      myEmit.emit(document, 'okok')