JS库的封装之三:添加事件

参考jQuery,使用JavaScript实现库的封装。本文为第三部分:事件封装。

一、需求

  参考jQuery中的实现逻辑,通过JavaScript代码手动实现:

  ● jQuery的on方法
    ৹ $(selector).on(“click”,function(){})
  ● jQuery的off方法
    ৹ $(selector).off(“click”)

二、on方法

  on方法用于给当前jQuery对象中的每一个DOM元素绑定事件
  使用方法:$("div").on("click",function(){})
  它有两个参数:事件类型(”click”等,字符串格式),回调函数。
  下面我们来实现on方法,此处我们只考虑IE8以上的浏览器。

    jQuery.fn.extend({
        on(type, callback) {
            // 给当前jQuery对象中的每一个DOM元素绑定事件
            this.each(function (index, element) {
                element.addEventListener(type, callback);
            });
            // 实现链式编程
            return this;
        }
    })

  测试代码:

    $("div").on("click", function (e) {
        // 获取点击的元素
        console.log(e.target);
        // this指向当前元素
        console.log(this);
    })

三、off方法

  off方法用于解除当前元素的所有指定事件
  使用方法:$("div").off("click")
  它只有一个参数:事件类型(”click”等,字符串格式)。

  此时会遇到一个问题,当我们调用 element.removeEventListener(type, callback); 时发现,我们并不能得到之前绑定事件的回调函数的地址。
  要想解决这个问题,必须在当初绑定事件的时候,把事件回调函数的内存地址保存起来。
  我们需要定义一个const数组(该变量对用户来说应该是私密的),在每次绑定事件时,将callback函数存进去。

  下面我们来实现off方法:

(function () {
    // 采用模块化的形式,使该变量称为这个模块的局部变量
    // events变量将会保存曾经绑定过的所有的事件处理函数
    // 由于事件处理函数在绑定好之后,是保存在浏览器的事件池中,这里只是引用了这个事件处理函数,所以这里不用担心内存浪费的问题,只有events对象占用了一小部分内存
    // 以DOM元素为区分,
    const events = [
        // { ele:div1,type:"click",callback:function(){} },
        // { ele:div1,type:"click",callback:function(){} },
        // { ele:div1,type:"keydown",callback:functioN(){} },
        // { ele:div3,type:"click",callback:function(){} }
    ];

    jQuery.fn.extend({
        // $("div").on("click",function(){})
        on(type, callback) {
            // 给当前jquery对象中的每一个DOM元素绑定事件
            this.each(function (index, element) {
                element.addEventListener(type, callback);
                events.push({ele: element, type, callback})
            });
            // 实现链式编程
            return this;
        },
        // 解绑绑定:$("div").off("click"):表示解除当前元素的所有的单击事件
        off(type) {
            this.each(function (index, element) {
                // 找到该元素曾经绑定过type类型的事件
                var evts = events.filter(function (evtObj) {
                    // 是否是该元素绑定的该类型的事件
                    var isCurrent = evtObj.ele === element && evtObj.type === type;
                    return isCurrent;
                });
                // 进行事件解绑操作
                evts.forEach(function (evt) {
                    var {callback} = evt;
                    element.removeEventListener(type, callback);
                })
            })
        }
    })
})()

  测试代码:

    $("div").on("click", function () {
        console.log('click div');
    });
    $("#div1").on("click", function () {
        console.log('click div1');
    });
    // 实现解绑div1元素的click事件
    $("#div1").off("click");  // 找到该元素该类型的事件总和

五、总结

  本文主要是仿照jQuery源码的功能,完成了on和off两个事件的封装。
  至此,框架封装的内容已经基本上结束了,后面将写一篇博客来对前面的所有内容进行复盘和整理。

  这个系列的文章,主要是通过仿照jQuery来学习JS库的封装,共写了如下几篇博客:
  1. 《JS库的封装之一:入口函数》
  2. 《JS库的封装之二:添加方法》
  3. 《JS库的封装之三:添加事件》
  4. 《JS库的封装之四:代码整合》


  目录