资讯专栏INFORMATION COLUMN

clipboard.js代码分析(3)- good-listener

objc94 / 933人阅读

摘要:上一篇文章介绍了这个工具库中的第二个依赖,这个工具库主要完成了一个简易的事件订阅发布器。节点事件绑定判断一个元素是否是节点,是通过构造函数和属性来判断的。

上一篇文章介绍了clipboard.js这个工具库中的第二个依赖tiny-emitter,这个工具库主要完成了一个简易的事件订阅发布器。这次介绍一下clipboard.js源码中的最后一个依赖的轻型工具库good-listener,这个工具库主要用来对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的原型上有一个matches方法,接受一个selector字符串,如果element元素被指定的字符串选择,那么返回true.

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

// document节点
const DOCUMENT_NODE_TYPE = 9
function 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实现展现出来。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93188.html

相关文章

  • clipboard.js代码分析(1)-select

    摘要:下面对它的实现一一分析。可以使用获取选中的内容也可以使用获取一个用户选择的范围。在这里完成了对用户选中内容的一些操作,而且在不是表单无法触发事件的时候,也可以在指定区域监听事件来实时获取选中的内容完成复制等功能。 项目中用到了选中复制功能 showImg(https://segmentfault.com/img/bVY7dH?w=400&h=78); 就是点击按钮,复制左侧的内容到剪切...

    li21 评论0 收藏0
  • clipboard.js代码分析(2)-emitter

    摘要:用于在同一主模块下的不同子模块以及不同主模块之间的通信,支持动态绑定作用域。如果用过的父子组件事件通信以及,对事件管理器应该不会陌生的。而且支持指定作用域,可以远程调用任意模块的函数。 上一篇文章介绍了clipboard.js这个工具库中的第一个依赖select这个工具库主要完成了对任意DOM元素的复制到粘贴板的功能。这次介绍一下clipboard.js源码中的第二个依赖的轻型工具库t...

    MoAir 评论0 收藏0
  • 在vue移动端项目中,配合clipboard.js插件,复制一段密令(类似淘宝支付宝口令)的文本到剪

    摘要:需求开发过程中遇到的需求需要复制请求过来的一段密令类似支付宝淘宝口令到剪切板。网上搜了下,是个相对比较靠谱的第三方。感谢大佬的评论意见使用该方法,会在下次更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的。 需求 开发过程中遇到的需求需要复制 请求过来的一段密令(类似支付宝淘宝口令)到剪切板。 pc端的文本复制到剪切板 如果是在pc端的话,可以直接使用原生js进...

    brianway 评论0 收藏0
  • 在vue移动端项目中,配合clipboard.js插件,复制一段密令(类似淘宝支付宝口令)的文本到剪

    摘要:需求开发过程中遇到的需求需要复制请求过来的一段密令类似支付宝淘宝口令到剪切板。网上搜了下,是个相对比较靠谱的第三方。感谢大佬的评论意见使用该方法,会在下次更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的。 需求 开发过程中遇到的需求需要复制 请求过来的一段密令(类似支付宝淘宝口令)到剪切板。 pc端的文本复制到剪切板 如果是在pc端的话,可以直接使用原生js进...

    Lyux 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<