如何实现on与off

优质
小牛编辑
125浏览
2023-12-01

关于事件部分,我思考了很久,也参考了许多,到底如何能用一个很简单的方法实现一模一样的on、off呢?

最后我的设计思路是:

1.有一个全局存储所有Events的数组,存放每个dom元素上的事件。

2.给每个DOM一个guid的唯一标识符,通过这个guid来找出Events数组里的事件。

由于逻辑比较复杂,我们先来画个图看看。

事件设计流程图

首先,我们利用DOM可以增加自定义属性的原理,在它的身上存一个guid。

之后整个事件机制就根据这个guid来进行查找与存储。

接下来是代码部分

Kodo.Events = []; //事件绑定存放的事件
Kodo.guid = 0; //事件绑定的唯一标识

on: function(type, selector, fn) {
    if (typeof selector == 'function') {
        fn = selector; //两个参数的情况
        for (var i = 0; i < this.length; i++) {
            if (!this[i].guid) {
                this[i].guid = ++Kodo.guid;
                //guid 不存在,给当前dom一个guid

                Kodo.Events[Kodo.guid] = {};
                /*
                *给Events[guid] 开辟一个新对象
                *用于存储这个dom上的所有事件方法
                */

                Kodo.Events[Kodo.guid][type] = [fn]; //每个方法都是一个数组
                //给这个新对象,赋予事件数组 "click" : [fn1,fn2,...]

                bind(this[i], type, this[i].guid);//绑定事件

            } else {//guid存在的情况
                var id = this[i].guid;
                if (Kodo.Events[id][type]) {
                    //如果这存在是当前事件已经存过,不用在绑定事件,直接放入方法数组即可
                    Kodo.Events[id][type].push(fn);
                } else {
                    //这是存新事件,所以需要重新绑定一次
                    Kodo.Events[id][type] = [fn];
                    bind(this[i], type, id);
                }
            }
        }
    }
}

function bind(dom, type, guid) {
    dom.addEventListener(type, function(e) { //绑定相应事件
        for (var i = 0; i < Kodo.Events[guid][type].length; i++) {
            //循环执行那个方法数组即可
            Kodo.Events[guid][type][i].call(dom, e); //正确的dom回调
        }
    }, false);
}

由于方法过长,我就把讲解的都写在了代码里,这样看的也会更方便一些。

代码还是不够形象!我们来看看log就能更清晰明白其中的奥秘。

通过控制台log出f.Events 发现正是我们想要的结果,每个dom对应一个自己的evtObj, 通过Kodo.Events[guid] 可以得到指定的evtObj。然后即可取出自己相应的事件。

绑定事件demo

如果我继续新增事件

绑定事件demo1

可以发现,我只针对于第一个li增加了事件。log出Evnets也就只有第一个Object有新增,并且会增加到对应的事件数组里。

理解了这个后要解除事件绑定,那就非常简单了。同样根据guid查找到对应的方法数组,delete即可

off: function(type, selector) {
    if (arguments.length == 0) {
        //如果没传参数,清空所有事件
        for (var i = 0; i < this.length; i++) {
            var id = this[i].guid;
            for (var j in Kodo.Events[id]) {
                delete Kodo.Events[id][j];
            }
        }
    } else if (arguments.length == 1) {
        //指定一个参数,则清空对应type的事件
        for (var i = 0; i < this.length; i++) {
            var id = this[i].guid;
            delete Kodo.Events[id][type];
        }
    } 
}

一个没有带有事件委托的on、off就可以这样实现了。

那如果我们要实现带委托的怎么办呢?

我们可以用这同样的思路实现,只是要多进行一个指定selector的存储。

这个我们就放在下一课最后讲解。

star是尊重作者知识果实最好的回报 :)