欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[原创] jQuery源码分析-10事件处理-Event-概述和基础知识

程序员文章站 2022-03-02 11:15:48
...

作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com

声明:本文为原创文章,如需转载,请注明来源并保留原文链接。 

 

读读写写,不对的地方请告诉我,多多交流共同进步,本章的的PDF下载在最后。

 

前记:

 

关于缺少示例,首先感谢这位朋友的反馈。在读源码的过程中,必然要写很多例子来跟踪代码的调用过程,经常还需要单步调试,遇到难度的地方,比如动画、AJAX要反复尝试,尽量覆盖各个分支,这个过程如果用文字记录下来的话会很长,而且未必能讲清楚,因为不像DEMO看效果就可以了,最好的办法是录成视频,这个有点遥远看时间和学的情况吧。

 

关于入门的示例,有的朋友建议多做一些入门教程,我的建议是,去看官网,官网有很全的DEMO,详细的解释,其他的入门教程也是抄官网的,官网上的很多评论很有价值值得仔细看看

 

关于造*,有的人在分析的过程中,会尝试用原生js去实现一个类似的功能,这是个好习惯;本系列的目的是深入的去分析jQuery的思想和技巧,如果理解透彻了模仿一个类似的自然水到渠成,我会在分析的过程将思路、用到的原生js都分解出来。重复造*是好事,而且必须要重复造,不重复造怎能明白原创的精髓,不重复造怎么能创新,关键的关键在于:多造少用,造出来的*暂时让它留在实验室里,就像我的篮球老师(同事)告诉我的,在平时练球时,要多用你不熟悉的那只手,但是比赛时要用你擅长的那只手,就是多练少用。直到你的双手都娴熟了,直到你的*达到原创的水平了,才能把它应用到项目中。

 

 

本章后续小节预告:

封装事件对象 接口应用 接口调用链 源码解析 DOMReady专题

 

10. 事件处理

10.1        概述

做为JavaScript库,首先要解决浏览器事件兼容问题,正确的管理事件,并提供便捷的接口方便开发;jQuery的事件模型以简单优雅的方式实现了这些需求:

n  自定义jQuery.Event对象,模拟实现W3C标准的DOM 3级别事件模型,统一了事件的属性和方法

n  可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数

n  提供了常用事件的便捷方法,例如 $( selector ).click( function … );同时支持通过代码触发事件,例如 $( selector ).click( )

n  支持自定义事件

n  扩展了组合事件:toggleclick时顺序执行绑定的事件)、hover(鼠标移入、移除)

n  扩展了one(只执行一次)、live-die(延迟绑定)、delegate-undelegate(类似live

n  提供了统一的事件封装、绑定、执行、销毁机制

n  优化DOM ready事件

事件的管理不外乎绑定、触发、销毁(详见下一节知识),jQuery则在浏览器原生的支持上进行了封装和完善。

jQuery并没有将事件处理函数直接绑定到DOM元素上,而是通过$.data存储在缓存$.cahce上;首先为DOM元素分配一个唯一ID,绑定的事件存储在$.cahce[ 唯一ID ][ $.expand ][ 'events' ]上,而events是个键-值映射对象,键就是事件类型,对应的值就是由事件处理函数组成的数组;最后在DOM元素上绑定(addEventListener/ attachEvent)一个事件处理函数eventHandle,这个过程由 jQuery.event.add 实现。

当事件触发时eventHandle被执行,eventHandle再去$.cache中寻找曾经绑定的事件处理函数并执行,这个过程由 jQuery.event. trigger jQuery.event.handle实现。

事件的销毁则由jQuery.event.remove 实现,remove对缓存$.cahce中存储的事件数组进行销毁,当缓存中的事件全部销毁时,调用removeEventListener/ detachEvent销毁绑定在DOM元素上的事件处理函数eventHandle

10.2        基础知识

在我们对jQuery的事件模型进行介绍和分析之前,如果读者不熟悉浏览器的事件模型,建议先读一读《JavaScript权威指南》第17章 事件和事件处理。以下是关键内容的笔记:

l  0级事件模型(所有浏览器都支持的实际标准API

  n  方式一:作为JavaScript属性的事件句柄(将函数直接绑定到事件属性上)

elment.onclick = function(){

    // ...

}

  n  方式二:作为HTML属性的事件句柄(将函数句柄以字符串的方式直接赋值给DOM元素的HTML属性)

<input type="button" value="0级事件模型" onclick="alert(this.nodeName);" />

l  2级事件模型(除Internet Explorer以外的所有现在浏览器都支持2DOM事件模型)

  n  事件传播分三个阶段进行:

    u  捕捉阶段,事件从Document对象沿着文档树向下传播给目标节点。如果目标的任何一个祖先(不是目标自身)专门注册了捕捉事件句柄,那么在事件传递过程中,就会运行这些句柄。

    u  目标阶段,直接注册在目标上的适合的事件句柄将运行。这与0级事件模型提供的事件处理方法相似

    u  起泡阶段,事件将从目标元素向上传播回或起泡回Document对象的文档层次。

虽然所有事件都受事件传播的捕捉阶段的支配,但并非所有类型的事件都起泡。一般来说,原始输入事件起泡(大部分事件),而高级语义事件不起泡(blue focus load unload

  n  注册一个事件句柄 Element.addEventListener( String type, Function listener, boolean useCapture )

    u  type 事件监听器调用所针对的时间类型。例如,load click mousedown

    u  listener 当指定类型的事件分派给这个元素的时候,事件监听器函数被调用。调用的时候,这个监听器函数被传递给一个Event对象,然后作为在该元素上注册的一个方法来调用

    u  useCapture 如果为true,那么只有在事件传播的捕捉阶段,指定的listener才会被调用。更常用的值是false,意味着在捕捉阶段不会调用listener,而当该节点是实际的事件目标或事件从它的原始目标起泡到该节点时,调用listener

该方法将指定的事件监听器函数添加到当前节点的监听器函数结合中,以处理指定类型type的事件。如果useCapturetrue,则监听器被注册为捕捉阶段监听器。如果useCapturefalse,它就注册普通事件监听器

addEventListener()可能被调用多次,在同一个节点上为同一种类型的事件注册多个事件句柄。但要注意,DOM不能确定多个事件句柄被调用的顺序。

如果一个事件监听器函数在同一个节点上用相同的typeuseCapture参数注册了两次,那么第二次注册将被忽略。如果正在处理一个节点上的事件时,在这个节点上注册了一个新的事件监听器,则不会为那个事件调用新的事件监听器。

当用Node.cloneNode()方法或Document.importNode()方法复制一个Document节点时,不会复制为原始节点注册的事件监听器。

  n  删除一个事件句柄 Element.removeEventListener( String type, Function listener, boolean useCapture )

    u  type 要删除事件监听器的事件类型

    u  listener 要删除的事件监听器函数

    u  useCapture 如果要删除是捕捉事件监听器,则为true;如果要删除的是普通事件监听器,则为false

该方法将删除指定的事件监听器函数。参数typeuseCapture必须与调用addEventListener()方法的相应参数一致。如果没有找到与指定的参数匹配的事件监听器,该方法什么都不做。

如果一个事件监听器函数被该方法删除,那么当节点发生指定类型的事件时,就不再调用它。即使一个事件监听器被同一个节点上同类型事件注册的另一个事件监听器删除,它也不再被调用。

  n  停止事件传播 Event.stopPropagation()

  n  阻止默认动作 Event.preventDefault()

l  IE事件模型

  n  注册一个事件句柄 Element.attachEvent( String type, Function listener )

    u  type 事件监听器调用所针对的事件的类型,带有一个“on”前缀。例如,onload onclick onmousedown

    u  listener 当指定类型的事件分派给这个元素的时候,事件监听器函数被调用。不会为这个函数传递任何参数,但是它可以从Window对象的event属性获得Event对象。

这个方法是一个特定与IE的事件注册方法。它和标准的addEventListener()方法(IE不支持它)具有相同的作用,但是,它和该函数也有一些重要的差别:

    u  由于IE事件模型不支持事件捕获,所以attachEvent()detachEvent()只需要两个参数:事件类型和句柄函数

    u  传递给IE方法的事件句柄名应该包含on前缀。例如,和attachEvent()一起使用的是onlick,而不是和addEventListener()一起使用的click

    u  使用attachEvent()注册的函数调用的时候没有Event对象参数。相反,它们必须读取Window对象的event属性

    u  使用attachEvent()注册的函数作为全局函数调用,而不是作为事件发生其上的文档元素的方法调用。也就是说,当使用attachEvent()注册的一个事件句柄执行的时候,this关键字引用的是Window对象,而不是事件目标元素。

    u  attachEvent()允许同一个事件句柄函数注册多次。当制定类型的一个事件发生的时候,注册的哈苏调用次数与它注册的次数相同

  n  删除一个事件监听器 Event.detachEvent( String type, Function listener )

    u  type 要删除的事件监听器所针对的事件的类型,带有一个on前缀。例如 onclick

    u  listener 要删除事件监听器函数

这个方法解除掉由attachEvent()方法所执行的事件句柄函数注册。它是removeEventListener()方法的特定与IE的替代。要为一个元素删除一个事件函数句柄,只需要使用你最初传递attachEvent()的相同参数来调用detachEvent()

  n  停止事件传播 window.event.cancelBubule = true;

  n  阻止默认动作 window.event.returnValue = false