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

上一篇文章引见了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);
      });
    }
  }
}

事宜托付的完成

简朴回忆一下事宜托付,比方有以下场景

<div class="wrapper">
    <p class='delegate'>
        <span>1</span>
    </p>
    <p class='delegate'>
        <span>2</span>
    </p>
    <p class='delegate'>
        <span>3</span>
    </p>
    <p class='delegate'>
        <span>4</span>
    </p>
</div>

须要把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完成展示出来。

    原文作者:dongzhe3917875
    原文地址: https://segmentfault.com/a/1190000013547261
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞