1

适用场景

在日常开发过程中,我们经常会遇到这样一种场景:我需要通过ajax从后端获取数据后动态添加dom节点来展示数据,并且这些dom节点有时候又需要是可交互的,例如点击事件。
那么在我们获取到数据之前这些dom节点是不存在的,也就是说我们没办法在获取数据之前给这个dom节点绑定任何事件。这个时候就需要事件委托来处理这类问题。

所谓的事件委托,简单的来说就是将一个元素响应事件委托给另外一个元素。
而正好浏览器当中有事件冒泡机制,一张图简单了解下浏览器的事件响应机制。
clipboard.png

  • 捕获阶段:在事件冒泡的模型中,捕获阶段不会响应任何事件;
  • 目标阶段:目标阶段就是指事件响应到触发事件的最底层元素上;
  • 冒泡阶段:冒泡阶段就是事件的触发响应会从最底层目标一层层地向外到最外层(根节点),事件委托即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层;

简单的说就是,我们使用document.addEventListener给dom节点绑定事件时会有个可选参数useCapture,设置为true就代表只执行事件捕捉,默认为false就代表默认是会有事件冒泡的。

因此我们跟事件委托结合起来就可以将子元素的事件委托给父元素,我们可以将事件绑定在父元素上,让他来判断子元素的情况并决定要不要执行该事件。

委托的优点

设想一下,假如后端返回1000个商品数据,获取到之后每个商品又希望他是可点击的,那么这时候如果给1000个商品都绑定事件的话,那消耗的内存可想而知是海量的。这时候事件委托就十分有意义了,你只需给一个父元素绑定事件,再执行的期间再去判断子元素情况看是否要执行你想要的事件。

并且,委托是可以动态绑定事件的,而正常的绑定事件都是需要获取到具体的dom节点之后才可进行。

简易封装

说这么多,我们最后简单来实现一下事件委托函数吧。
核心思路就是,父元素触发相应后判断子元素是否满足函数所传入的选择器钩子,满足的情况下就说明该元素需要触发该事件。

/**
 * @param {绑定的事件名称} event 
 * @param {想要绑定的子元素,可传入class或者标签名或者id} selector 
 * @param {委托的父元素,可传入class或者id,建议传入id} pSelector 
 * @param {事件回调函数} callback 
 * @param {回调函数需要传入的参数} data 
 */
function delegation(event, selector, pSelector, callback, data) {
    var isId = selector.indexOf('#') === 0;
    var isTagName = selector.indexOf('.') === -1;
    var isClassName = selector.indexOf('.') === 0;
    var parent = document.querySelector(pSelector);
    parent.addEventListener(event, function(e) {
        var target = e.target;
        if (isClassName) {
            // 如果传入class名
            if (target.className === selector.substr(1)) callback(data);
        } else if (isTagName) {
            // 如果传入标签名
            if (target.nodeName.toLocaleLowerCase() === selector) callback(data);
        } else if (isId) {
            // 如果传入id名
            if (target.id === selector.substr(1)) callback(data);
        }
    });
}
delegation('click', 'div', 'body', function(data){
    console.log('click =>', data); // click => {test: 1}
}, {test: 1});

以上是简单实现的委托函数,没有处理各种浏览器兼容性问题。主要是为了简单演示下绑定机制~


Kyrielin
1.1k 声望330 粉丝

Coding is amazing!