博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
clipboard.js代码分析(3)- good-listener
阅读量:6263 次
发布时间:2019-06-22

本文共 4325 字,大约阅读时间需要 14 分钟。

文章介绍了这个工具库中的第二个依赖,这个工具库主要完成了一个简易的事件订阅发布器。这次介绍一下clipboard.js源码中的最后一个依赖的轻型工具库,这个工具库主要用来对dom的事件绑定进行一层封装,支持事件委托delegate,和jquery的写法非常类似,源码简洁且清晰易懂,对理解事件绑定模型和原理非常有帮助。

快速用法

const listen = require('good-listener')

good-listener支持常见的三种方式来绑定事件

  • node节点事件绑定
var logo = document.getElementById('logo');listen(logo, 'click', function(e) {    console.log(e);});
  • nodeList多个节点遍历事件绑定
var anchors = document.querySelectorAll('a');listen(anchors, 'click', function(e) {    console.log(e);});
  • 字符串形式的委托事件绑定(默认委托的对象document.body)
listen('.btn', 'click', function(e) {    console.log(e);});

代码实现

good-listener的实现大致如下

function listen(target, type, callback) {  if (is.node(target)) {    return listenNode(target, type, callback);  } else if (is.nodeList(target)) {    return listenNodeList(target, type, callback);  } else if (is.string(target)) {    return listenSelector(target, type, callback);  } else {    throw new TypeError(`argument must be a    String, HTMLElement, HTMLCollection, or NodeList`);  }}

对于nodenodeList节点的实现比较简单,一笔带过,这里主要分析一下delegate委托的实现。

node节点事件绑定

判断一个元素是否是node节点,是通过构造函数constructornodeType属性来判断的。

value !== undefined && value instanceof HTMLElement && value.nodeType === 1;

listenNode实现,返回了一个对象,为事件绑定扩展了一个取消绑定的方法destroy

function listenNode(node, type, callback) {  node.addEventListener(type, callback);  return {    destroy: function() {      node.removeEventListener(type, callback);    }  }}

nodeList多个节点遍历事件绑定

判断一个元素是否是nodeList节点,是通过构造函数constructorlength属性来判断的。且需要保证类数组中的元素都是有效的dom节点

var type = Object.prototype.toString.call(value)value !== undefined &&    (type === '[object NodeList]' || type === '[object HTMLCollection]') &&    ('length' in value) &&    (value.length === 0 || exports.node(value[0]))

listenNodeList实现, 遍历一下类数组,一次执行事件绑定即可

function listenNodeList(nodeList, type, callback) {  Array.prototype.forEach.call(nodeList, function(node) {    node.addEventListener(type, callback);  });  return {    destroy: function() {      Array.prototype.forEach.call(nodeList, function(node) {        node.removeEventListener(type, callback);      });    }  }}

事件委托的实现

简单回顾一下事件委托,比如有以下场景

1

2

3

4

需要把p标签的点击事件委托到父元素div上面,jquery中的写法是

$('.wrapper').on('click', '.delegate', function(e) {...})

如果我们自己去实现,是不能单纯的去判断点击的target是不是包含delegate,因为点击的元素有可能是子元素span,所以子元素可以通过冒泡找到delegate,那么委托也是应该可以触发的。

再介绍一个方法,下面要用到,Element的原型上有一个方法,接受一个selector字符串,如果element元素被指定的字符串选择,那么返回true.

1.首先来模拟这个冒泡的过程

// document节点const DOCUMENT_NODE_TYPE = 9function closet (element, selector) {    while(element && element.nodeType !== DOCUMENT_NODE_TYPE) {        if (typeof element.matches === 'function' && element.matches(selector)) {            return element        }        element = element.parentNode    }}

这个函数就是判断点击的元素是否能够向上冒泡(不断的获取父元素)匹配到指定的委托元素。

结合上面例子就是span能否向上找到delegate

2.使用代理模式为事件的回调函数封装一层判断逻辑,当符合委托逻辑的时候,才去执行回调函数

并且为event对象添加一个属性delegateTarget,这样在event中可以拿到对应的三个对象

  • thise.currentTarget 是被委托的元素
  • e.delegateTarget 是委托元素
  • e.target 是点击元素
function listener(element, selector, type, callback) {  return function(e) {    e.delegateTarget = closest(e.target, selector);    if (e.delegateTarget) {      callback.call(element, e);    }  }}

3.delegate内部封装

let _delegate = (element, selector, type, callback, useCapture) => {  // 将element和selector封装一层  let listenerFn = listener.apply(this, arguments)  element.addEventListener(type, listenerFn, useCapture)  return {    destroy () {      element.removeListener(type, listenerFn, useCapture)     }  }}

4.delegate

分为两种情况,被委托可以是一个dom元素,也可以是css选择器字符串,如下所示

delegate(document.body, '.btn', 'click', function(e) {    console.log(e.delegateTarget);}, false);
delegate('.container', '.btn', 'click', function(e) {    console.log(e.delegateTarget);}, false);

分别处理即可

let delegate = (elements, selector, type, callback, useCapture) => {  if (typeof elements.addEventListener === 'function') {    return _delegate.apply(null, arguments)  }  if (typeof elements === 'string') {    elements = document.querySelectorAll(elements)  }  return Array.prototype.map.call(elements, function (element) {    return _delegate(element, selector, type, callback, useCapture)  })}

结语

本次介绍的good-listener这个工具库的用法和源码分析,介绍了nodenodeList的事件绑定封装的实现,尤其探求了一下委托的实现方法,用不多的代码实现了兼容性很好的delegate,至此clipboard.js的依赖都介绍完了,下一篇文章会整合代码,将完整的clipboard.js实现展现出来。

转载地址:http://gckpa.baihongyu.com/

你可能感兴趣的文章
洛谷P1481 魔族密码(LIS)
查看>>
SQL Server 访问URL 调用WebServer
查看>>
静态代码块在何时调用
查看>>
Kafka控制器选举流程剖析
查看>>
appium封装显示等待Wait类和ExpectedCondition接口
查看>>
Android 全局弹出版本更新 Dialog 思考和解决办法
查看>>
IDEA在当前类中查找方法快捷键--转
查看>>
初识少儿编程
查看>>
浏览器 UA 判断
查看>>
理解OAuth 2.0
查看>>
高并发处理思路与手段(三):消息队列
查看>>
Docker+Nginx部署Angular
查看>>
Docker & ASP.NET Core (4):容器间的连接
查看>>
beam 的异常处理 Error Handling Elements in Apache Beam Pipelines
查看>>
将png图片转换为字体图标
查看>>
/var/log/wtmp
查看>>
C# 获取机器码
查看>>
什么是医嘱?医嘱的书写内容?
查看>>
如何通过CSP编程卸载Windows Mobile应用程序
查看>>
用delphi实现完美屏幕截图
查看>>