zoukankan      html  css  js  c++  java
  • 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]}
            */
    

      

    心有多大,世界就有多大,让青春随着梦飞翔。
  • 相关阅读:
    docker容器里面执行top报“TERM environment variable not set.”
    docker安装ubuntu以后无ifconfig命令解决办法
    免sudo使用docker
    centos6下安装docker
    Hadoop Aggregate Resource Allocation解释
    [] 与[[]]的区别
    awk如何传递外部变量?
    基于PaddlePaddle的语义匹配模型DAM,让聊天机器人实现完美回复 |
    作为一名程序员,在面试中如何展现你Python的coding能力?
    入门机器学习:代码+预训练模型,赶快练练吧!
  • 原文地址:https://www.cnblogs.com/xcmylrl/p/5405797.html
Copyright © 2011-2022 走看看