• javascript事件系统的发展史


    一个完整的事件系统,通常存在以下三个角色:

    • 事件对象,用于储存事件的状态。
    • 事件源对象,当前事件在操作的对象,如元素节点,文档对象,window对象,XMLHttpRequest对象等。
    • 事件监听器,当一个事件源生成一个事件对象时,它会调用相应的回调函数进行操作。在IE中,事件对象恒为全局属性window.event的分身。

    在w3c没有把其DOM 模型引入网页时,netscape与微软已经逼不及待到快他们熟悉的语言中把相关的DOM模型搞进来了。这其实也怪javascript之父忙于把抄袭其他语言,忽略了自身事件系统的建设。从此世界被划分为两大阵营了。

    DOM0时代,这里的DOM指w3c的DOM。双方都设计两种绑定事件的方法,无侵入式与侵入式。你可以说内联式与非内联式的区别。

    侵入式,双方都一样。没有办法,那是很早就实现的。那时IE只有抄袭的份,还不敢胡来。

    <input name="ruby" onclick="alert(this.nam)" />
    

    然后是无侵入式,这估计是它们都完成了各自的DOM模型,实现对元素节点的索引机制之后的事了。比如有以下网页片断:

        
    <input type="button" name="button1" value="aaaa"/>

    我们必须自上而下,一步步找到此元素节点才能操作它。注意,那时没有所谓的document.getElementById。网景的做法,把相关绑定的代码放进一个script标签中:

        <form name="form1">
          <input
            type="button"
            name="button1"
            value="aaaa"/>
        </form>
    

    如果你不想把代码用window.onload = function(){}这代码块括起来,那么你得把这script标签放于表单元素之后。

    微软也有一套索引机制,基本与网景的一样,但IE4还引入了document.all与document.all.tags。不过IE还有另一套方式:

       <script
          for="button1"
          event="onclick"
          language="JavaScript">
            alert("this.aaa")
        </script>
    

    不过,它用不了this(或者能,我不会),另要求一个script标签对应body中的一个标签,实在很浪费,最终被淘汰出局了。

    这就是DOM0的绑定机制,另以内联方式写在标签中的代码,其实相当于以下方式:

        <p id="aa" onclick="alert('aaaa')">相当于↓</p>
        <script type="text/javascript">
          var p = document.getElementById("aa")
          p.onclick = new Function("alert('aaaa')")//相当于↓
          p.onclick = function(){alert('aaaa')}
        </script>
    

    至此,事件系统三个角色都出场了。通过索引机制得到的对象(元素节点什么的),作为事件源,onclick,onmousemove之类的事件属性,它们充当监听器,onclick后面的函数就是回调函数,这是异步执行的。

    随着无侵入的兴起,放到web标准中,应该叫做表现行为结构相分离。在标签内写onclick什么的应该唾弃。无侵入式编程有一种让人越写越多代码的欲望。以前总是缩在一个标签内,随时注意双引号与单引号的套嵌,写多了就烦了,不想写了,现在没有这限制,就像脱缰的马,把更多注意力用于兼容更多浏览器与创造新的点子上。好了,写着写着,人们就开始想能不能在同一个元素上绑定两个onclick事件呢?!

        <script type="text/javascript">
          var p = document.getElementById("aa")
          p.onclick = function(){alert('第一次')}
          p.onclick = function(){alert('第二次')}
        </script>
    

    当然,只能alert第二个,我们当然也可以用一些技巧达到这目的:

        <p id="aa" onclick="alert('第一次')">能绑定多个同类型函数</p>
        <script type="text/javascript">
          var p = document.getElementById("aa")
          var addEvent = function(el,type,fn) {
            var type = "on"+type
            var old = el[type];
            if (typeof el[type] != 'function') {
              el[type] = fn
            }else {
              el[type] = function() {
                old();
                fn();
              }
            }
          }
          addEvent(p,"click",function(){alert('第二次')});
          addEvent(p,"click",function(){alert('第三次')});
       </script>
    

    但当要用户搞这东西是不行,因此浏览器商把它们做成内置的。顺带还搞了个事件流,也就是允许事件对象在控件间(标签)中传递。IE的一套API是createEventObject, attachEvent, dettachEvent, fireEvent,事件流是自下向上。网景那套就不清楚了,但听说w3c也是从它那一套发展而来,API比较复杂,createEvent, initEvent,addEventListener, removeEventListener dispatchEvent,那个initEvent还有许多版本呢,如initMouseEvent, initKeyEvent,参数非常多,用于更精确的配置。addEventListener拥有三个参数,但第三个参数通常只在事件代理中有用,通常为false,与IE保持一致,自下而上的冒泡。由于w3c的劣性根,总想与IE划分界线,它最高能冒泡到window(IE为document):

            event = dom.Event(type);
            args =  [event].concat(args);
            var parent = caller;
            while(!event.isPropagationStopped() && parent){//isPropagationStopped为w3c dom的一个方法,
              dom.events.handle.apply( parent,args);     //判定是否已禁止冒泡
              parent = parent.parentNode || (parent != window) && window;
            }
    

    很奇怪的是HTML的parentNode竟然是文档对象。如果是捕获就麻烦多了,这里不谈它。现在看一下多事件绑定时的兼容问题吧。比如上面那个addEvent其实够用了,DE大神的addEvent也是根据DOM0事件搞出来的。但有一些事件是DOM0绝对模拟不了,如FF的DOMMouseScroll事件,因为没有onDOMMouseScroll这个属性,它必须要用addEventListener,但IE,opera,chrome等支持的mousewheel。因此我们还是离不开这些高级的API。一个通用addEvent函数:

    var addEvent = (function () {
        if (document.addEventListener) {
            return function (el, type, fn) {
                el.addEventListener(type, fn, false);
            };
        } else {
            return function (el, type, fn) {
                el.attachEvent('on' + type, function () {
                    return fn.call(el, window.event);
                });
            }
        }
    })();
    

    不过还是有问题,IE下绑定回调函数不是先进先出,详见《IE与非IE浏览器在事件绑定的执行顺序问题》。嗯,这些我将留在下一部分讲。

    PS,这个系列与《javascript 跨浏览器的事件系统》系列是不一样,这里着重讲述设计一个事件系统遇到的各种各样的问题。而后者则给出具体的解决方案。

  • 相关阅读:
    Linux kernel device mapper
    草莓网
    openwrt系统源码地址
    ubuntu 安装eclipse for c++
    ubuntu下安装eclipse IDE for C/C++ developers
    Makefile 中:= ?= += =的区别
    core dump
    rtp
    skbuff
    A Neural Algorithm of Artistic Style
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/1721988.html
Copyright © 2020-2023  润新知