【JS】自定义JS工具函数库-封装DOM事件监听addEventListener-自定义事件总线eventBus-自定义消息订阅与发布PubSub

文章目录

  • 1. 封装DOM事件监听addEventListener
    • 1.1 复习DOM事件流
    • 1.2 事件冒泡
    • 1.3 事件委托
    • 1.4 自定义DOM事件监听
  • 2. 自定义事件总线eventBus
    • 定义
    • 使用
  • 3. 自定义消息订阅与发布PubSub
    • 定义
    • 使用

关于 自定义JS工具类 相关类似的源码请看 https://gitee.com/ykang2020/my_utils

1. 封装DOM事件监听addEventListener

1.1 复习DOM事件流

基础知识可以参考这篇博文

【DOM】JavaScript-事件高级-注册事件-事件流-事件对象-事件冒泡-委派-鼠标键盘事件

简单复习一下DOM事件流

  1. 捕获阶段
  2. 当前目标阶段
  3. 冒泡阶段

【JS】自定义JS工具函数库-封装DOM事件监听addEventListener-自定义事件总线eventBus-自定义消息订阅与发布PubSub_第1张图片
JS代码中只能执行捕获或者冒泡其中的一个阶段

addEventListener(type, listener[, useCapture]) 中的第三个参数
如果是 true,表示在事件捕获阶段调用事件处理程序;
如果是 false(默认值),表示在事件冒泡阶段调用事件处理程序。

let innerBox = document.getElementById('inner')
let outterBox = document.getElementById('outter')

outterBox.addEventListener('click', function () {
     
  console.log('捕获 outter')
},true)

innerBox.addEventListener('click', function () {
     
  console.log('捕获 inner')
},true)

outterBox.addEventListener('click', function () {
     
  console.log('冒泡 outter')
})

innerBox.addEventListener('click', function () {
     
  console.log('冒泡 inner')
})

点击蓝色盒子产生的效果
【JS】自定义JS工具函数库-封装DOM事件监听addEventListener-自定义事件总线eventBus-自定义消息订阅与发布PubSub_第2张图片

1.2 事件冒泡

事件在目标元素上处理后, 会由内向外(上)逐层传递

事件冒泡的应用场景: 事件代理/委托/委派

1.3 事件委托

没有将事件绑定在目标元素身上,而是
多个子元素的同类事件监听委托给(绑定在)共同的一个父组件上

这样做的好处:

  1. 减少内存占用(事件监听回调从n变为1)
  2. 动态添加的内部元素也能响应

1.4 自定义DOM事件监听

/**
 * 自定义事件监听
 * @param {String} el 父元素选择器
 * @param {String} type 事件类型
 * @param {Function} fn 回调函数
 * @param {String} selector 子元素选择器
 */
export default function myAddEventListener(el, type, fn, selector) {
     
  // 判断el的类型,获取元素
  if (typeof el === "string") {
     
    el = document.querySelector(el);
  }
  // 事件判定
  // 若没有传第四个参数(子元素选择器),则给el元素绑定事件
  if (!selector) {
     
    el.addEventListener(type, fn);
  } else {
     
    el.addEventListener(type, function(e) {
     
      // 获取点击的目标事件源
      const target = e.target;
      // 判断选择器与目标元素是否相符
      if (target.matches(selector)) {
     
        // 相符就执行回调,否则就什么都不做
        fn.call(target,e);
      }
    });
  }
}
<ul>
  <li>AAAli>
  <li>BBBli>
  <li>CCCli>
  <li>DDDli>
  <div>EEE divdiv>
ul>
import myAddEventListener from "./eventBind/myAddEventListener";

myAddEventListener(
  "ul",
  "click",
  function () {
     
    console.log(this.innerHTML);
  },
  "li"
);

【JS】自定义JS工具函数库-封装DOM事件监听addEventListener-自定义事件总线eventBus-自定义消息订阅与发布PubSub_第3张图片

2. 自定义事件总线eventBus

  • eventBus: 包含所有功能的事件总线对象
  • eventBus.on(eventName, callback): 绑定事件监听
  • eventBus.emit(eventName, data): 分发事件
  • eventBus.off(eventName): 解绑指定事件名的事件监听, 如果没有指定解绑所有

定义

// 定义事件总线
const eventBus = {
     
  // 保存eventName类型与回调callback的容器
  callbackObj: {
     },
};

/**
 * 绑定事件监听,保存回调函数
 * @param {*} eventName 事件名称
 * @param {*} callback 回调函数
 */
eventBus.on = function (eventName, callback) {
     
  if (this.callbackObj[eventName]) {
     
    // 如果 callbackObj 属性中存在该类型事件,就压入
    this.callbackObj[eventName].push(callback);
  } else {
     
    // 如果 callbackObj 属性中不存在该类型事件,就添加成数组
    this.callbackObj[eventName] = [callback];
  }
};

/**
 * 分发事件,触发容器中的该属性eventName的回调函数
 * @param {*} eventName 事件名称
 * @param {*} data 数据
 */
eventBus.emit = function (eventName, data) {
     
  // 判断callbackObj中有没有回调函数
  if (this.callbackObj[eventName] && this.callbackObj[eventName].length > 0) {
     
    // 遍历数组
    this.callbackObj[eventName].forEach((callback) => {
     
      // 执行回调函数
      callback(data);
    });
  }
};

/**
 * 移除事件监听
 * @param {*} eventName 事件名称
 */
eventBus.off = function (eventName) {
     
  // 若传入了 eventName 事件类型
  if (eventName) {
     
    // 只是删除 eventName 对应的事件回调
    delete this.callbackObj[eventName];
  } else {
     
    // 否则全部删除
    this.callbacksObj = {
     };
  }
};

export default eventBus;

使用

import eventBus from "./eventBind/eventBus";

eventBus.on("login", (data) => {
     
  console.log(data + "用户已经登陆");
});
// 可以为一个事件绑定多个回调函数
eventBus.on("login", (data) => {
     
  console.log(data + "登陆数据已经写入");
});

// 触发事件
setTimeout(() => {
     
  eventBus.emit("login", "yk");
}, 2000);

// 绑定 + 触发
eventBus.on("delete", (data) => {
     
  console.log("delete", data);
});
eventBus.emit("delete", "hehe");

// 绑定+触发+删除+触发
eventBus.on("add", (data) => {
     
  console.log("add", data);
});
eventBus.emit("add", "YK");
eventBus.off("add");
console.log(eventBus);
eventBus.emit("add", "ykykyk");

【JS】自定义JS工具函数库-封装DOM事件监听addEventListener-自定义事件总线eventBus-自定义消息订阅与发布PubSub_第4张图片

3. 自定义消息订阅与发布PubSub

PubSub: 包含所有功能的订阅/发布消息的管理者
PubSub.subscribe(msg, subscriber): 订阅消息: 指定消息名和订阅者回调函数
PubSub.publish(msg, data): 异步发布消息: 指定消息名和数据
PubSub.publishSync(msg, data): 同步发布消息: 指定消息名和数据
PubSub.unsubscribe(flag): 取消订阅: 根据标识取消某个或某些消息的订阅

定义

const PubSub = {
     
  // 订阅编号
  id: 1,
  // 频道与回调保存的容器
  callbacks: {
     
    // 例子
    // pay: {
     
    //   token_1: fn,
    //   token_2: fn2,
    // },
  },
};

/**
 * 订阅频道
 * @param {*} channel 频道
 * @param {*} callback 回调
 */
PubSub.subscribe = function (channel, callback) {
     
  // 为每一个订阅创建唯一的编号
  let token = "token_" + this.id++;
  if (this.callbacks[channel]) {
     
    // 如果callbacks中有这个频道就直接压入
    this.callbacks[channel][token] = callback;
  } else {
     
    // 没有就创建一个
    this.callbacks[channel] = {
     
      [token]: callback,
    };
  }
  // 返回频道订阅的ID
  return token;
};

/**
 * 异步执行 发布消息
 * @param {*} channel 频道
 * @param {*} data 数据
 */
PubSub.publish = function (channel, data) {
     
  // 获取当前频道所有回调
  if (this.callbacks[channel]) {
     
    // 启动定时器异步执行任务
    setTimeout(() => {
     
      // Object.values() 返回对象值的数组
      Object.values(this.callbacks[channel]).forEach((callback) => {
     
        callback(data);
      });
    }, 0);
  }
};

/**
 * 同步执行 发布消息
 * @param {*} channel
 * @param {*} data
 */
PubSub.publishSync = function (channel, data) {
     
  // 获取当前频道所有回调
  if (this.callbacks[channel]) {
     
    // Object.values() 返回对象值的数组
    Object.values(this.callbacks[channel]).forEach((callback) => {
     
      callback(data);
    });
  }
};

/**
 * 取消订阅
 * 三种情况
 * 1. 没有传值 全删
 * 2. 传的是id 删id对应的频道
 * 3. 传的是频道名 删该频道
 * @param {*} flag
 */
PubSub.unsubscribe = function (flag) {
     
  if (flag === undefined) {
     
    // 清空所有订阅
    this.callbacks = {
     };
  } else if (typeof flag === "string") {
     
    // 判断是否是 token_ 开头的字符串
    if (flag.indexOf("token_") === 0) {
     
      // 是订阅id
      // 遍历callbacks 找到具体的token 对象值的数组用find方法找到有flag的对象
      let callbackObj = Object.values(this.callbacks).find((obj) =>
        obj.hasOwnProperty(flag)
      );
      // 判断是不是存在
      if (callbackObj) {
     
        // 有这个对象
        delete callbackObj[flag];
      }
    } else {
     
      // 是频道名称
      delete this.callbacks[flag];
    }
  } else {
     
    throw new Error("如果传入参数, 必须是字符串类型");
  }
};

export default PubSub;

使用

import PubSub from "./eventBind/myPubSub";

// 订阅消息
let pId1 = PubSub.subscribe("pay", (data) => {
     
  console.log("商家接到订单,准备开始制作", data);
});
let pId2 = PubSub.subscribe("pay", (data) => {
     
  console.log("骑手接到订单,准备开始取餐", data);
});

// 异步发布一个消息
PubSub.publish("pay", {
     
  title: "盖浇饭",
  price: 18,
  pos: "学校",
});

// 取消订阅
PubSub.unsubscribe(pId1);

console.log(PubSub);

【JS】自定义JS工具函数库-封装DOM事件监听addEventListener-自定义事件总线eventBus-自定义消息订阅与发布PubSub_第5张图片
关于 自定义JS工具类 相关类似的源码请看 https://gitee.com/ykang2020/my_utils

你可能感兴趣的:(JavaScript,javascript,js)