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

384 阅读2分钟

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

今天被实习生问到事件捕获冒泡、代理和派发的问题,举举例子讲了一番后,然后有了此文 作一个记录

目录大纲

  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第三个参数不写,默认冒泡)
    <script type="text/javascript">
    var sonEle = document.getElementById('sonEle');
    var parEle = document.getElementById('parEle');
    
    // 测试例子1:(全冒泡,先子后父)
    parEle.addEventListener('click', function () {
        console.log('父父父父父父级111111111');
    }); 
    sonEle.addEventListener('click', function (e) {
        console.log('子级111');
        // e.stopPropagation()  // 阻止冒泡,可以解开注释 调试看看
    });
    parEle.addEventListener('click', function () {
      console.log('父父父父父父级');
    });
    sonEle.addEventListener('click', function () {
      console.log('子级');
    });
    </script>
    
    点击“---我是子元素---”,打印:
    子级111
    子级
    父父父父父父级111111111
    父父父父父父级
    
    点击“我是父元素”,打印:
    父父父父父父级111111111
    父父父父父父级
    
  2. 测试例子2:(全捕获,先父后子)(addEventListener第三个参数写 true)
    <script type="text/javascript">
    var sonEle = document.getElementById('sonEle');
    var parEle = document.getElementById('parEle');
    
    // 测试例子2:(全捕获,先父后子)
    parEle.addEventListener('click', function (e) {
        console.log('父父父父父父级111111111');
        // e.stopPropagation()  // 阻止捕获,可以解开注释 调试看看
    }, 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
    父父父父父父级
    
  3. 测试例子3:(前2个冒泡,后2个捕获)
    <script type="text/javascript">
    var sonEle = document.getElementById('sonEle');
    var parEle = document.getElementById('parEle');
    
    // 测试例子3:(前2个冒泡,后2个捕获)
    parEle.addEventListener('click', function () {
        console.log('父父父父父父级111111111');
    }); 
    sonEle.addEventListener('click', function () {
        console.log('子级111');
    });
    parEle.addEventListener('click', function () {
      console.log('父父父父父父级');
    }, true);
    sonEle.addEventListener('click', function () {
      console.log('子级');
    }, true);
    </script>
    
    点击“---我是子元素---”,打印:
    父父父父父父级
    子级
    子级111
    父父父父父父级111111111
    
    点击“我是父元素”,打印:
    父父父父父父级
    父父父父父父级111111111
    

2. 事件代理示例

好处:不用一个个绑定子元素事件了,直接绑父元素,然后用event.target来判断对应的子元素

<!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()。

要触发自定义事件需要用dispatchEvent 事件派发

  • dom.dispatchEvent,可以派发给对应的dom
  • dispatchEvent只接受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. 原理介绍:发布订阅者模式

可看我的另一篇: juejin.cn/post/695425…

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')

码字不易,点赞鼓励