Ruby's Louvre

每天学习一点点算法

导航

IE与非IE浏览器在事件绑定的执行顺序问题

原始的DOM1事件处理机制,是不能绑定多个处理函数。如一个元素在onclick事件先绑定一个函数,用来alert其id,然后再在相同的事件绑定另一个函数,用来alert其style。对不起,第二个函数会覆盖掉第一个,只会alert其style。于是.addEventListener() 与 .attachEvent()被分别开发出来了。为了屏蔽各浏览器的差异,javascript界举办了一个慈善邀请赛,鼓励大家提交各自addEvent/removeEvent 方案,最后由John Resig大神获得。

  function addEvent( obj, type, fn ) {
    if ( obj.attachEvent ) {
      obj["e"+type+fn] = fn;
      obj[type+fn] = function(){obj["e"+type+fn]( window.event );}
      obj.attachEvent("on"+type, obj[type+fn] );
    } else
      obj.addEventListener( type, fn, false );
  }
  function removeEvent( obj, type, fn ) {
    if ( obj.detachEvent ) {
      obj.detachEvent("on"+type, obj[type+fn] );
      obj[type+fn] = null;
    } else
      obj.removeEventListener( type, fn, false );
  }

但是最近我在同一个元素绑定多个onclick事件时,发现些问题。

IE8与其他标准浏览器会弹出1,2,3,4,换言之,先进先出。IE6非先进先出,也不是后进先出,而是2,4,3,1。但如果我们绑定更多函数,IE8的执行顺序就有问题了,IE6与IE7更不用说。谁有好的解决方法吗?

  /**
   * addEvent & removeEvent -- cross-browser event handling
   * Copyright (C) 2006-2007  Dao Gottwald
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   *
   * Contact information:
   *   Dao Gottwald  <dao at design-noir.de>
   *
   * @version  1.2.1
   */

  function addEvent(o, type, fn) {
    o.addEventListener(type, fn, false);
  }
  function removeEvent(o, type, fn) {
    o.removeEventListener(type, fn, false);
  }
  /*@cc_on if (!window.addEventListener) {
  var addEvent = function (o, type, fn) {
    if (!o._events) o._events = {};
    var queue = o._events[type];
    if (!queue) {
      o._events[type] = [fn];
      if (!o._events._callback)
        o._events._callback = function (e) { Event._callListeners(e, o) };
      o.attachEvent("on" + type, o._events._callback);
    } else if (Event._fnIndex(o, type, fn) == -1)
      queue.push(fn);
    else return;
    Event._mem.push([o, type, fn]);
  };
  var removeEvent = function (o, type, fn) {
    var i = Event._fnIndex(o, type, fn);
    if (i < 0) return;
    var queue = o._events[type];
    if (queue.calling) {
      delete queue[i];
      if (queue.removeListeners)
        queue.removeListeners.push(i);
      else
        queue.removeListeners = [i];
    } else
      if (queue.length == 1)
        Event._detach(o, type);
      else
        queue.splice(i, 1);
  };
  var Event = {
    AT_TARGET: 2,
    BUBBLING_PHASE: 3,
    stopPropagation: function () { this.cancelBubble = true },
    preventDefault: function () { this.returnValue = false },
    _mem: [],
    _callListeners: function (e, o) {
      e.stopPropagation = this.stopPropagation;
      e.preventDefault = this.preventDefault;
      e.currentTarget = o;
      e.target = e.srcElement;
      e.eventPhase = e.currentTarget == e.target ? this.AT_TARGET : this.BUBBLING_PHASE;
      switch (e.type) {
        case "mouseover":
          e.relatedTarget = e.fromElement;
          break;
        case "mouseout":
          e.relatedTarget = e.toElement;
      }
      var queue = o._events[e.type];
      queue.calling = true;
      for (var i = 0, l = queue.length; i < l; i++)
        if (queue[i])
          if ("handleEvent" in queue[i])
            queue[i].handleEvent(e);
          else
            queue[i].call(o,e);
      queue.calling = null;
      if (!queue.removeListeners)
        return;
      if (queue.length == queue.removeListeners.length) {
        this._detach(o, e.type);
        return;
      }
      queue.removeListeners = queue.removeListeners.sort(function(a,b){return a-b});
      var i = queue.removeListeners.length;
      while (i--)
        queue.splice(queue.removeListeners[i], 1);
      if (queue.length == 0)
        this._detach(o, e.type);
      else
        queue.removeListeners = null;
    },
    _detach: function (o, type) {
      o.detachEvent("on" + type, o._events._callback);
      delete o._events[type];
    },
    _fnIndex: function (o, type, fn) {
      var queue = o._events[type];
      if (queue)
        for (var i = 0, l = queue.length; i < l; i++)
          if (queue[i] == fn)
            return i;
      return -1;
    },
    _cleanup: function () {
      for (var m, i = 0; m = Event._mem[i]; i++)
        if (m[1] != "unload" || m[2] == Event._cleanup)
          removeEvent(m[0], m[1], m[2]);
    }
  };
  addEvent(window, "unload", Event._cleanup);
} @*/

后来,又发现Dean Edwards也修正了IE事件顺序错乱问题

  // addEvent/removeEvent written by Dean Edwards, 2005
  // with input from Tino Zijdel
  // http://dean.edwards.name/weblog/2005/10/add-event/
  function addEvent(element, type, handler) {
    if (!handler.$$guid) handler.$$guid = addEvent.guid++;
    if (!element.events) element.events = {};
    var handlers = element.events[type];
    if (!handlers) {
      handlers = element.events[type] = {};
      if (element["on" + type]) {
        handlers[0] = element["on" + type];
      }
    }
    handlers[handler.$$guid] = handler;
    element["on" + type] = handleEvent;
  };
  addEvent.guid = 1;
  function removeEvent(element, type, handler) {
    if (element.events && element.events[type]) {
      delete element.events[type][handler.$$guid];
    }
  };
  function handleEvent(event) {
    var returnValue = true;
    event = event || fixEvent(window.event);
    var handlers = this.events[event.type];
    for (var i in handlers) {
      this.$$handleEvent = handlers[i];
      if (this.$$handleEvent(event) === false) {
        returnValue = false;
      }
    }
    return returnValue;
  };
  function fixEvent(event) {
    event.preventDefault = fixEvent.preventDefault;
    event.stopPropagation = fixEvent.stopPropagation;
    return event;
  };
  fixEvent.preventDefault = function() {
    this.returnValue = false;
  };
  fixEvent.stopPropagation = function() {
    this.cancelBubble = true;
  };
  function addEvent(element, type, handler) {
    if (!handler.$$guid) handler.$$guid = addEvent.guid++;
    if (!element.events) element.events = {};//一个巨大的哈希,
    var handlers = element.events[type];
    if (!handlers) {
      handlers = element.events[type] = {};//哈希的每一个键都是哈希
      if (element["on" + type]) {
        handlers[0] = element["on" + type];
      }
    }
    handlers[handler.$$guid] = handler;
    element["on" + type] = function(event) {
      var returnValue = true;
      event = event || window.event;
      var handlers = this.events[event.type];
      for (var i in handlers) {
        this.$$handleEvent = handlers[i];
        if (this.$$handleEvent(event) === false) {
          returnValue = false;
        }
      }
      return returnValue;
    };
  };
  addEvent.guid = 1;

posted on 2009-09-20 14:42  司徒正美  阅读(4914)  评论(6编辑  收藏  举报