您的位置:首页 > Web前端

浏览器兼容问题项目总结(三)事件处理机制

2017-04-05 16:14 543 查看
由于项目需要,本系列文章基于IE7兼容模式和Chrome49这两种浏览器内核版本所做兼容。

1、IE和Chrome下绑定事件处理方式:

IE:attachEvent(‘onresize’,function);

Chrome: addEventListener(resize,funtion,false);

事件处理兼容性解决方法:

if(window.attachEvent){window.attachEvent('onresize',function(){setTimeout('wMask.winResized();',50);});window.attachEvent('onunload',function(){wMask =null;});}
else{ window.addEventListener('resize',function(){setTimeout('wMask.winResized();',50);},false);window.addEventListener(function(){wMask =null;},'unload',false);}
相应的解绑:

IE: detachEvent( '事件名' ,函数)    

Chrome: removeEventListener( '事件名' , 函数, 布尔值)    布尔值: [  true表示在事件捕获阶段执行  , false表示在事件冒泡阶段执行 ]

2、jQuery下事件绑定机制:

从jQuery1.7开始,jQuery引入了全新的事件绑定机制,on()和off()两个函数统一处理事件绑定。因为在此之前有bind(), live(), delegate()等方法来处理事件绑定,jQuery从性能优化以及方式统一方面考虑决定推出新的函数来统一事件绑定方法并且替换掉以前的方法。

替换bind()

当第二个参数'selector'为null时,on()和bind()其实在用法上基本上没有任何区别了,所以我们可以认为on()只是比bind()多了一个可选的'selector'参数
$('a').bind('click', myHandler);
$('a').on('click', myHandler);

$(window).unbind('scroll.myPlugin');
$(window).off('scroll.myPlugin');

替换live()

在1.4之前相信大部分非常喜欢使用live(),因为它可以把事件绑定到当前以及以后添加的元素上面,当然在1.4之后delegate()也可以做类似的事情了。live()的原理很简单,它是通过document进行事件委派的,因此也可以使用on()通过将事件绑定到document来达到live()一样的效果。
live()写法
代码如下:
 $('#list li').live('click', '#list li', function() {
    //function code here.
}); 
on()写法

代码如下:
$(document).on('click', '#list li', function() {
    //function code here.
});
这里的关键就是第二个参数'selector'在起作用了。它是一个过滤器的作用,只有被选中元素的后代元素才会触发事件。

替换delegate()
delegate()是1.4引入的,目的是通过祖先元素来代理委派后代元素的事件绑定问题,某种程度上和live()有点相似。只不过live()是通过document元素委派,而delegate则可以是任意的祖先节点。使用on()实现代理的写法和delegate()基本一致。
delegate()的写法

代码如下:
$('#list').delegate('li', 'click', function() {
    //function code here.
});
on()写法

代码如下:
$('#list').on('click', 'li', function() {
    //function code here.
});
区别是第一个和第二个参数的顺序颠倒了一下,别的基本一样。
1)jQuery1.7后 on事件绑定的方式:

$( "elem" ).on( events, [selector], [data], handler );

events:事件名称,可以是自定义事件名称

selector:选择器

data:事件触发时传递给事件处理函数

handler:事件处理函数

2)on方法源码分析

on: function( types, selector, data, fn, /*INTERNAL*/ one ) {

  var origFn, type;

  // 如果types是对象,则说明是传入了多个事件

  if ( typeof types === "object" ) {

    // 如果selector不是string,则说明用户没有传入selector

    if ( typeof selector !== "string" ) {

      // 把selector赋值给data

      data = data || selector;

      // selector置为undefined

      selector = undefined;

    }

    // 遍历types对象中的每一个元素,并递归调用自身

    for ( type in types ) {

      this.on( type, selector, data, types[ type ], one );

    }

    return this;

  }

  // 如果data和fn都为null,说明用户只传了前两个参数

  if ( data == null && fn == null ) {

    // 把selector(第二个参数)赋值给fn

    fn = selector;

    // data和selector置为undefined

    data = selector = undefined;

  // 如果fn为null,说明用户传了三个参数

  } else if ( fn == null ) {

    // 如果selector的类型是string,说明用户没传data

    if ( typeof selector === "string" ) {

      // 把data赋值给fn

      fn = data;

      // 把data置为undefined

      data = undefined;

    } else {

      // 否则的话,说明用户没传selector,而是传了data,将data赋值给fn

      fn = data;

      // 将selector赋值给data

      data = selector;

      // 将selector置为undefined

      selector = undefined;

    }

  }

  // 如果用户传入的事件处理函数是false值,则将事件处理函数赋值为jQuery内部的returnFalse函数

  if ( fn === false ) {

    fn = returnFalse;

  // 如果用户没传回调函数,返回this,this是啥?返回this干嘛?

  } else if ( !fn ) {

    return this;

  }

  // 如果one为1,内部用,只绑定一次,暂时没看到用途

  if ( one === 1 ) {

    origFn = fn;

    fn = function( event ) {

      // Can use an empty set, since event contains the info

      jQuery().off( event );

      return origFn.apply( this, arguments );

    };

    // Use same guid so caller can remove using origFn

    fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );

  }

  // 遍历this对象,调用jQueryevent对象的add方法处理事件

  return this.each( function() {

    jQuery.event.add( this, types, fn, data, selector );

  });

},

通过分析on方法的源码发现,on方法并没有处理事件相关的任何事情,只是对用户传入的参数进行调整,真正处理事件的是其内部的jQuery.event对象的add方法

3)event对象的构造函数都做了什么工作

jQuery.Event = function( src, props ) {

  // 余老板的suggest组件中也用到了这种方法

  // 检测this是不是Event对象,如果不是,new一个Event对象出来,这样就避免了外部new对象

  if ( !(this instanceof jQuery.Event) ) {

    return new jQuery.Event( src, props );

  }

  // 如果有src,并且src有type属性

  if ( src && src.type ) {

    // 定义originalEvent属性并将src赋值给它

    this.originalEvent = src;

    // 定义type属性,并将src.type赋值给它

    this.type = src.type;

    // 定义isDefaultPrevented属性并通过判断事件被阻止冒泡为其赋值

    this.isDefaultPrevented = ( src.defaultPrevented ||

      src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;

  // 否则,将src赋值给type

  } else {

    this.type = src;

  }

  // 如果用户传入props,则扩展Event或者覆盖原有的属性

  if ( props ) {

    jQuery.extend( this, props );

  }

  // 创建一个时间戳

  this.timeStamp = src && src.timeStamp || jQuery.now();

  // 给这个Event对象一个标记

  this[ jQuery.expando ] = true;

};

4)jQuery的event对象封装的方法和属性

  jQuery的event对象提供了如下方法和属性:

    {

      add : function(){},

      remove : function(){},

      trigger : function(){},

      dispatch : function(){},

      handlers : function(){},

      fix: function(){},

      simulate : function(){},

      global : {},

      props : {},

      fixHooks : {},

      keyHooks : {},

      mouseHooks : {},

      special : {}

    }

  首先看add方法:

  add: function( elem, types, handler, data, selector ) {

    var handleObjIn, eventHandle, tmp,

      events, t, handleObj,

      special, handlers, type, namespaces, origType,

      elemData = data_priv.get( elem );

    // 涉及到jQuery的另外一个大的方面:缓存机制。

    // 不为text、comment节点绑定数据,直接返回

    if ( !elemData ) {

      return;

    }

    // 如果handler是一个有handler属性或方法的对象,则进行一些转移赋值操作

    if ( handler.handler ) {

      handleObjIn = handler;

      handler = handleObjIn.handler;

      selector = handleObjIn.selector;

    }

    // 检查handler是否有一个唯一的id,方便之后查找和删除

    if ( !handler.guid ) {

      // 如果没有就为其设定一个唯一id

      handler.guid = jQuery.guid++;

    }

    // 如果elemData中没有events对象,则为其定义events属性并赋值为空对象

    if ( !(events = elemData.events) ) {

      events = elemData.events = {};

    }

    // 如果elemData中没有handle对象

    if ( !(eventHandle = elemData.handle) ) {

      // 为elemData定义一个handle方法(事件处理函数)

      eventHandle = elemData.handle = function( e ) {

        // 有点迷糊。。。

        return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?

          jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :

          undefined;

      };

      // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events

      eventHandle.elem = elem;

    }

    // 处理types中传入的是通过空格分割的多个事件的情况

    types = ( types || "" ).match( core_rnotwhite ) || [""];

      t = types.length;

      while ( t-- ) {

        tmp = rtypenamespace.exec( types[t] ) || [];

        type = origType = tmp[1];

        namespaces = ( tmp[2] || "" ).split( "." ).sort();

        if ( !type ) {

          continue;

      }

      // 事件是否会改变当前状态,如果是则使用特殊事件,看event的special属性。。。

      special = jQuery.event.special[ type ] || {};

      // 根据是否有selector判断使用哪种特殊事件(看完特殊事件再过来看这个地方)

      type = ( selector ? special.delegateType : special.bindType ) || type;

      // 根据新的type获取新的special

      special = jQuery.event.special[ type ] || {};

      // 组装用于特殊事件处理的对象

      handleObj = jQuery.extend({

        type: type,

        origType: origType,

        data: data,

        handler: handler,

        guid: handler.guid,

        selector: selector,

        needsContext: selector && jQuery.expr.match.needsContext.test( selector ),

        namespace: namespaces.join(".")

      }, handleObjIn );

      // 如果是第一次调用,初始化事件处理队列(将同一事件的处理函数放入数组中)

      if ( !(handlers = events[ type ]) ) {

        handlers = events[ type ] = [];

        handlers.delegateCount = 0;

        // 如果获取特殊事件监听方法失败,则使用addEventListener添加事件(抛弃attachEvent了吗?)

        if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {

          if ( elem.addEventListener ) {

            elem.addEventListener( type, eventHandle, false );

          }

        }

      }

      // 使用special的add方法进行处理

      if ( special.add ) {

        special.add.call( elem, handleObj );

        if ( !handleObj.handler.guid ) {

          handleObj.handler.guid = handler.guid;

        }

      }

      if ( selector ) {

        handlers.splice( handlers.delegateCount++, 0, handleObj );

      } else {

        handlers.push( handleObj );

      }

      jQuery.event.global[ type ] = true;

    }

    // 清空elem,避免内存泄漏

    elem = null;

  },
小结:

jQuery推出on()的目的有两个,一是为了统一接口,二是为了提高性能,所以从现在开始用on()替换bind(), live(), delegate吧。尤其是不要再用live()了,因为它已经处于不推荐使用列表了,随时会被干掉。如果只绑定一次事件,那接着用one()吧,这个没有变化。推荐使用on() off().

注:jQuery1.9删除了一些在1.8中已经过时的api

jQuery1.9删除了一些在1.8中已经过时的api,想要把那些不够安全的、缺乏效率的、用处不大的,以及带有误导的特性统统去掉。如果你想升级你的jquery版本,但又使用了如下被删除的api的话,可以引入Migrate迁移插件,被删除的api可以在Migrate插件中修复。

.toggle(function, function, … )
toggle()方法可实现点击一个元素来执行指定函数。此方法被删除以减少混乱和提高潜在的模块化程度。

jQuery.browser()
jQuery.browser()通过UA信息来检测浏览器。此方法在1.3版本中已不赞成使用,并在1.9版本中被删除,jquery团队更建议通过特性检测来时您的代码兼容浏览器。

.live()
live()方法可以为未来生成的元素添加事件,此方法在1.7版本中已不赞成使用,并在1.9版本中被删除,您可以使用on()方法来代替。

.die()
die()方法可以移除live()添加的事件,此方法在1.7版本中已不赞成使用,并在1.9版本中被删除,您可以使用off()方法来代替。

.sub()
sub()方法可以可创建一个新的jQuery副本,不影响原有的jQuery对像,但用例的数量已经不足以证明它存在的价值。
3、jQuery下事件处理

bind()   向元素添加事件处理程序   

blur()   添加/触发 blur 事件   

change()   添加/触发 change 事件   

click()   添加/触发 click 事件   

dblclick()    添加/触发 doubleclick 事件   

delegate()    向匹配元素的当前或未来的子元素添加处理程序   

die()   在版本 1.9 中被移除。移除所有通过 live() 方法添加的事件处理程序   

error()   在版本 1.8 中被废弃。添加/触发 error 事件   

event.currentTarget    在事件冒泡阶段内的当前 DOM 元素   

event.data    包含当前执行的处理程序被绑定时传递到事件方法的可选数据   

event.delegateTarget    返回当前调用的 jQuery 事件处理程序所添加的元素   

event.isDefaultPrevented()    返回指定的 event 对象上是否调用了event.preventDefault()   

event.isImmediatePropagationStopped()    返回指定的 event 对象上是否调用了event.stopImmediatePropagation()   

event.isPropagationStopped()    返回指定的 event 对象上是否调用了event.stopPropagation()   

event.namespace    返回当事件被触发时指定的命名空间   

event.pageX    返回相对于文档左边缘的鼠标位置   

event.pageY    返回相对于文档上边缘的鼠标位置   

event.preventDefault()    阻止事件的默认行为   

event.relatedTarget    返回当鼠标移动时哪个元素进入或退出   

event.result    包含由被指定事件触发的事件处理程序返回的最后一个值   

event.stopImmediatePropagation()    阻止其他事件处理程序被调用   

event.stopPropagation()    阻止事件向上冒泡到 DOM 树,阻止任何父处理程序被事件通知   

event.target    返回哪个 DOM 元素触发事件   

event.timeStamp    返回从 1970 年 1 月 1 日到事件被触发时的毫秒数   

event.type    返回哪种事件类型被触发   

event.which    返回指定事件上哪个键盘键或鼠标按钮被按下   

event.metaKey    事件触发时 META 键是否被按下   

focus()   添加/触发 focus 事件    

focusin()   添加事件处理程序到 focusin 事件   

focusout()    添加事件处理程序到 focusout 事件   

hover()   添加两个事件处理程序到 hover 事件   

keydown()   添加/触发 keydown 事件   

keypress()    添加/触发 keypress 事件   

keyup()   添加/触发 keyup 事件   

live()   在版本 1.9 中被移除。添加一个或多个事件处理程序到当前或未来的被选元素   

load()   在版本 1.8 中被废弃。添加一个事件处理程序到 load 事件   

mousedown()    添加/触发 mousedown 事件   

mouseenter()    添加/触发 mouseenter事件   

mouseleave()    添加/触发 mouseleave事件   

mousemove()    添加/触发 mousemove 事件   

mouseout()    添加/触发 mouseout 事件   

mouseover()    添加/触发 mouseover 事件   

mouseup()   添加/触发 mouseup 事件   

off()   移除通过 on() 方法添加的事件处理程序   

on()   向元素添加事件处理程序   

one()   向被选元素添加一个或多个事件处理程序。该处理程序只能被每个元素触发一次   

$.proxy()   接受一个已有的函数,并返回一个带特定上下文的新的函数   

ready()   规定当 DOM 完全加载时要执行的函数   

resize()   添加/触发 resize 事件   

scroll()   添加/触发 scroll 事件   

select()   添加/触发 select 事件   

submit()   添加/触发 submit 事件   

toggle()   在版本 1.9 中被移除。添加 click 事件之间要切换的两个或多个函数   

trigger()   触发绑定到被选元素的所有事件   

triggerHandler()    触发绑定到被选元素的指定事件上的所有函数    

unbind()   从被选元素上移除添加的事件处理程序   

undelegate()    从现在或未来的被选元素上移除事件处理程序   

unload()   在版本 1.8 中被废弃。添加事件处理程序到 unload 事件   

contextmenu()    添加事件处理程序到 contextmenu事件   

$.holdReady()    用于暂停或恢复.ready() 事件的执行
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  前端 chrome 浏览器