• js中事件(自定义事件)


      今天闲的蛋疼,我们来聊一聊web前端中的事件机制和自定义事件。灵感来自jQuery,在此感谢jQuery作者。

      首先,最开始。

    <button id="button" type="button" onclick="alert('hello')">你好</button>
    

      这是我们在使用html写页面的时候最原生的事件触发方式。上面那行代码会生成一个按钮,当我们点击这个按钮的时候就会弹出一个原生的弹窗,内容是hello。

      随着技术的发展,我们认为事件要和html结构分开,于是就演化出了这么一种写法。

    <button id="button" type="button">你好</button>
    <script>
    var button = document.getElementById("button");
    button.onclick = function(){
      alert("hello"); } </script>

      以上代码的效果和第一个一样,但是实现了事件与html的分离。

      和上面的代码采用一样的原理,可以为各种各样元素添加各种各样事件。比如说keyup、mouseover等。

      那我们对js最原生的事件有了一定的了解后,我们就会想,我们能不能自定义事件呢?比如说,我们希望在按一个按钮的时候触发一个save事件。我们发现原生的js中没有save事件,怎么办,难道就这么放弃吗?

      于是我们就考虑了,事件的本质在于消息的传递。那我们把save写成一个函数,当我们点击按钮的时候执行该函数,不就变相的实现了这个自定义事件吗?

    <button id="button" type="button">你好</button>
    <script>
    var button = document.getElementById("button");
    button.onclick = function(){
         save();
    }
    var save = function(){
         alert("save");
    }
    </script>
    

      是啊,实现是实现了,但我们就觉得这个方法好挫啊,而且我们如果想要为save定义多个事件,就会发现,后一个事件会覆盖前一个事件这就相当的蛋疼了。

      那我们可不可以这样,将save事件弄成一个函数数组,在触发的时候顺序触发这个数组中的每一个函数,这样我们不就可以触发多个方法了?然后我们如果需要为该事件添加新的方法,只要在这个数组中添加新的项就可以啦。

    <button id="button" type="button">你好</button>
    <script>
    var button = document.getElementById("button");
    button.onclick = function(){
        trigger(save);
    }
    var trigger = function(){
        for(var i in save){
            save[i]();
        }
    }
    var save1 = function(){
        alert("save1");
    }
    var save2 = function(){
        alert("save2");
    }
    var save = [save1,save2];
    </script>
    

      以上的代码会顺序弹出save1和save2。使用同样的方法我们可以为原生的事件添加多个函数方法。(是不是有点类似于addEventListener和attachEvent?)

      看着上面的代码还是有点不爽,为什么呢?因为没有上面提到的那两个方法帅呀。哈哈。

      我们的方法优势在于可以添加自定义事件,而原生的方法不但执行效率比我们高,使用也比咱们便利,感觉好不爽。

      我们重新设计一下,刚才说原生的方法比咱们便利,那我们就进行统一化尽力提高便利性。我们刚才的分析中提到了,原生的事件和自定义的事件都可以通过以上的方法来玩。那我们就不要管是原生的还是自定义的了。

      事件可能有哪几种操作,我这里只想到了,添加、移除、触发以及挂靠到原生事件上。那我们可以定义addEvent()添加事件、removeEvent()移除事件、trigger()触发事件、dispatchEvent()挂靠事件。

      这里就不提供源代码了,如果有兴趣可以去查看jQuery源码,推荐看低版本,比如说1.0.4那里的事件机制是最原始的,也是最易懂的。

      addEvent()

        1、需要检测事件数组是否存在,如果不存在定义一个数组,这个数组用于存放事件的所有方法,执行2。否则执行3

        2、将挂载在元素上的事件方法添加到该数组中。执行3

        3、将函数参数中传入的事件方法添加到事件数组中。执行4

        4、将函数数组挂载带元素上。

      removeEvent()

        1、通过传入参数找到事件数组中想要移除的事件方法。执行2

        2、移除对应事件方法。

      trigger()

        1、依次执行事件数组中的每一个函数方法即可。

      dispatchEvent()

        1、将事件数组的触发函数挂载到元素的执行函数上。只要完成下面代码的效果即可。

    <button id="button" type="button">你好</button>
    <script>
    var button = document.getElementById("button");
    var handler = function(type){
        //这是事件数组触发函数
    }
    button.onclick = handler(click);
    </script>
    

      写了这么多,还是感觉好不爽怎么办,为什么呢?我要是一次性触发了好多好多事件,那我们就不好理解这些事件的执行顺序了。

      于是,我们可以设置一个全局的事件队列,触发函数触发事件的时候,不直接执行函数方法,而是在事件队列中添加一个信号。而全局的事件队列定时的检测是否有新的事件产生(比如100毫秒检测一次,实际上不会带来多大的系统开销。)如果有新的事件产生,就执行对应的函数方法。这样的好处在于有利于用户控制每个事件的执行顺序(只要调整事件队列中的顺序即可),从而达到很多意想不到的效果。

    --------------------------------------------------分割线------------------------------------------

    后面会陆陆续续将jquery1.0.0版本中的event源码分析发上来。

    --------------------------------------------------分割线-------------------------------------------

    /*add函数用于添加事件,和上文中的addEvent用处相同。*/
            add: function(element, type, handler) {
                if ( jQuery.browser.msie && element.setInterval != undefined )
                    element = window;
                /*为每一个函数(handler)分配一个不重复的id作为访问句柄。
                 如果之前已经添加过该函数了就不在进行分配*/
                if ( !handler.guid )
                    handler.guid = this.guid++;
                /*每一个元素第一次进来的时候都会访问它,之后就再也不会访问。
                  用于初始化一个位于元素下的事件对象。*/
                if (!element.events)
                    element.events = {};
                /*type是事件类型,这里的目的是将事件对象中属于本次添加事件
                  类型的事件对象缓存下来。如果第一次添加该类型的事件则
                  handlers未定义。*/
                var handlers = element.events[type];
                /*当第一次添加该类型事件时,初始化该类型事件对象。注意,
                  如果元素原生事件上如果有对应类型的事件,记得把它存下来。*/
                if (!handlers) {
                    handlers = element.events[type] = {};
                    if (element["on" + type])
                        handlers[0] = element["on" + type];
                }
                /*将本次添加的函数的句柄保存到对应的函数对象中*/
                handlers[handler.guid] = handler;
                console.log(handlers[handler.guid])
                /*将事件分发函数挂载到元素的原生事件上*/
                element["on" + type] = this.handle;
                /*初始化一个全局的事件队列,将元素压到队列中,表示该元
                  素可以触发对应类型的事件。这里是为了触发的方便考虑。*/
                if (!this.global[type])
                    this.global[type] = [];
                this.global[type].push( element );
            },
            /*add函数执行完毕以后会生成以下内容
            *1、函数的句柄中会生成一个guid
            *例如 var fn1 = function(){}
            *     $.event.add( window, "click", fn1 );
            *执行以后console.log(fn1);//这里会有一个数字,不一定是
            *多少,但不重复,按事件的添加顺序,从1开始。
            *2、元素下会生成一个events
            *例如
            $("div").bind("mouseover",function(){
                console.log(1);
            }).bind("mouseover",function(){
                console.log(2);
            });
            $("div").bind("click",function(){
                console.log(1);
            }).bind("click",function(){
                console.log(2);
            });
            *会生成events形如
            *{
            *    "click":{4:function(){console.log(1);},5:function(){console.log(2);}},
            *    "mouseover":{2:function(){console.log(1);},3:function(){console.log(2);}
            *}
            *这里之所以没有1,是因为通常情况下会在window下有一个load事
            * 件,那个是最先加载的。
            *3、console.log(handlers[handler.guid])//function()就
            *是本次添加进来的事件函数的句柄
            *4、div.onclick指向事件分发函数
            *5、global中形如{"click":[div,div],"mouseover":[div,div]}
            */
    

      

    心有多大,世界就有多大,让青春随着梦飞翔。
  • 相关阅读:
    Leetcode 92. Reverse Linked List II
    Leetcode 206. Reverse Linked List
    Leetcode 763. Partition Labels
    Leetcode 746. Min Cost Climbing Stairs
    Leetcode 759. Employee Free Time
    Leetcode 763. Partition Labels
    搭建数据仓库第09篇:物理建模
    Python进阶篇:Socket多线程
    Python进阶篇:文件系统的操作
    搭建数据仓库第08篇:逻辑建模–5–维度建模核心之一致性维度2
  • 原文地址:https://www.cnblogs.com/xcmylrl/p/5405797.html
Copyright © 2020-2023  润新知