`
bencode
  • 浏览: 109253 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

jQuery源码解析3(Event)

    博客分类:
  • WEB
阅读更多

这一篇,我们要看 jQuery Events 部分的源码。

 

这一次看的是1.4的源码,因为API接口没变,但功能增强,并且live支持了所有事件, 支持context等。

 

我们平常这样写:

 

$('#button').click(function() {
    alert('you hurt me!');
});

 

也可以这样写:

 

$('#button').bind('click', function() {
    alert("don't touch me!");
});

 

想到click的实现应该基于bind, 搜索一下bind, 来到这里:

 

jQuery.each(["bind", "one"], function( i, name ) {
	jQuery.fn[ name ] = function( type, data, fn ) {
		// Handle object literals

 

看来bind 和 one 有点关系呀!它们的签名都是这样:

function(type, data, fn);

 

one 是啥?

 

$('#button').one('click', function() {
    alert('下次点我就不会有效果了!');
});

 

没问题,我们go on

 

		if ( typeof type === "object" ) {
			for ( var key in type ) {
				this[ name ](key, data, type[key], fn);
			}
			return this;
		}

 

在1.4中,我们可以一次绑定多个事件

 

$('#div').bind({
  click: function() {
  },
  mouseenter: function() {
  } 
});

 

接着往下看:

 

		if ( jQuery.isFunction( data ) ) {
			thisObject = fn;  // thisObject 哪里来的?
			fn = data;
			data = undefined;
		}

 

bind 和 one 的 签名是 function(type, [data], fn),  这个 if 让我们可以省略 data。
此外 thisObject = fn;  thisObject 哪里来的? 

 

我的感觉是作者可能想让 bind 中的事件能够绑定特定的scope。

如: (注,这段功能我是假想)

$('#button').bind('click', function() {
    this.name;  // 现在的this不是 button
}, { name: 123 });


但现在并没有实现,而且也不需要实现,因为有jQuery.proxy 帮助我们完成需要的事。
 如果实现,函数签名应该是:  function( type, data, fn, thisObject);

-----------

 

继续:

 

var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
			jQuery( this ).unbind( event, handler );
			return fn.apply( this, arguments );
		}) : fn;

 

这段代码对one进行特别处理, 创建一个新的代理事件方法,让其执行后进行解绑,以达到one的效果。

 

jQuery.proxy 是 1.4 中新添加的方法, 它可以生成一个代理function, 用于改变fn的scope。

 

	proxy: function( fn, proxy, thisObject ) {
		if ( arguments.length === 2 ) {
			if ( typeof proxy === "string" ) {
				thisObject = fn;
				fn = thisObject[ proxy ];
				proxy = undefined;
			} else if ( proxy && !jQuery.isFunction( proxy ) ) {
				thisObject = proxy;
				proxy = undefined;
			}
		}

		if ( !proxy && fn ) {
			proxy = function() {
				return fn.apply( thisObject || this, arguments );
			};
		}

		if ( fn ) {
			proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
		}
		return proxy;
	},

 

公开的API使用两个参数的版本:

 

 

var user = {
	name: "bena",
	say: function() {
		alert( this.name );
	 }
};

$("#button").click(jQuery.proxy(user, 'say'));

$("#button").click(jQuery.proxy(user.say, user));  

 

 

但在这里没有"正规使用", 仅仅为它们设置了一个guid。

 

写得白一点就是:

 

var handler  = fn;
if (name === 'one') {
	handler = function(event) {
		jQuery(this).unbind(event, handler);
		return fn.apply(this, arguments);
	};
	handler.guid = fn.guid = ...;
}

 

完成了这个之后,接着就要把任务交给 jQuery.event.add 啦

 

		return type === "unload" && name !== "one" ?
			this.one( type, data, fn, thisObject ) :
			this.each(function() {
				jQuery.event.add( this, type, handler, data );
			});


当 type == 'unload' 时: this.one(type, data, fn, thisObject);  // 看,这里是调用四个参数的,和我们上面的猜想一致(可能在下一版本中实现)

 

下面我们就要来看看重要的:

 

jQuery.event.add(elem, type, handler, data); 

 

 

jQuery.event = {
	add: function( elem, types, handler, data ) {
		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
			return;
		}

		// For whatever reason, IE has trouble passing the window object
		// around, causing it to be cloned in the process
		if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
			elem = window;
		}

		if ( !handler.guid ) {
			handler.guid = jQuery.guid++;
		}

 

 

nodeType == 3 表示文本text, nodeType  == 8 表示注释, 这两个不需要事件, 所以略过。

 

如果是window 对象, 对IE进行特别的处理(IE真是怪呀)

 

 

接着往下看:

 

		if ( data !== undefined ) {
			// Create temporary function pointer to original handler
			var fn = handler;

			// Create unique handler function, wrapped around original handler
			handler = jQuery.proxy( fn );

			// Store data in unique handler
			handler.data = data;
		}

 

如果有data, 会创建一个原事件方法的代理,然后设置data。因为我们可能会挂接一个handler到多个事件

 

 

继续:

 

 

		var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
			handle = jQuery.data( elem, "handle" ), eventHandle;

		if ( !handle ) {
			eventHandle = function() {
				return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
					jQuery.event.handle.apply( eventHandle.elem, arguments ) :
					undefined;
			};

			handle = jQuery.data( elem, "handle", eventHandle );
		}

		if ( !handle ) {
			return;
		}

		// Add elem as a property of the handle function
		// This is to prevent a memory leak with non-native
		// event in IE.
		handle.elem = elem;

 

这一段创建(获取)节点相关联的两个对象: events 和 handle (节点上有多个事件,则公用)

 

events 是一个普通对象。
handle 是一个函数, 函数体等用到的时候再看。

因为不是任何节点都能 jQuery.data,像(embed, object, applet) 所以上面有一个 if (!handle) 判断。

 

		types = types.split( /\s+/ );
		var type, i=0;
		while ( (type = types[ i++ ]) ) {
			// Namespaced event handlers
			var namespaces = type.split(".");
			type = namespaces.shift();
			handler.type = namespaces.slice(0).sort().join(".");

 

我们可以这样使用:

 

$('#button').bind('click mouseenter', function() {}); // 同时绑定多个事件。

 

自1.3以后,jQuery也支持namespace event。

 

可以这样调用:

 

$('#button').bind('click.abc.def', function() {});

 

此时 type = 'click',  handler.type = 'abc.def'

 

再往下看:

 

 

			var handlers = events[ type ],
				special = this.special[ type ] || {};

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

				if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
					if ( elem.addEventListener ) {
						elem.addEventListener( type, handle, false );
					} else if ( elem.attachEvent ) {
						elem.attachEvent( "on" + type, handle );
					}
				}
			}

 

先不管 handlers = events[type] 是啥, 反正一开始肯定是空的:)

我们的type也不special。

 

代码终于到了这里:

 

if ( elem.addEventListener ) { // DOM
	elem.addEventListener( type, handle, false );
} else if ( elem.attachEvent ) { // IE
	elem.attachEvent( "on" + type, handle );
}

 

我们看到, 事件终于在这里挂接,并且一个节点相同type的事件只挂接一次。

 

而事件触发时,将会调用 handle (就是这个上面被我们暂时忽略的方法,现在要仔细看):

 

		if ( !handle ) {
			eventHandle = function() {
				return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
					jQuery.event.handle.apply( eventHandle.elem, arguments ) :
					undefined;
			};

			handle = jQuery.data( elem, "handle", eventHandle );
		}
		if ( !handle ) {
			return;
		}

		// Add elem as a property of the handle function
		// This is to prevent a memory leak with non-native
		// event in IE.
		handle.elem = elem;

 

在页面unload后,jQuery 对象不存在了。jQuery.event.triggered 我们暂时也不用管。


handle 仅仅把操作代理给

handle = function() {
    jQuery.event.handle.apply(elem, arguments) :
};

 

 

2. jQuery.event.handle(event)

 

这里的event是浏览器相关的, 此函数的scope(即this), 是 elem。

 

	handle: function( event ) {
		var all, handlers;

		event = arguments[0] = jQuery.event.fix( event || window.event );
		event.currentTarget = this;

 

首先将 event 包装成 jQuery.Event 对象, 让其在所有浏览器上都有相同的属性和行为。

 

然后根据 type从节点找到 handlers,准备执行:

 

		var namespaces = event.type.split(".");
		event.type = namespaces.shift();

		all = !namespaces.length && !event.exclusive;

		var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");

		handlers = ( jQuery.data(this, "events") || {} )[ event.type ];

 

由浏览器触发的事件 type 不会包含namespace。

用户调用trigger时,可以包含namespace,  到时候再来看。 

 

下面就是执行handers了:

 

 

		for ( var j in handlers ) {
			var handler = handlers[ j ];
			if ( all || namespace.test(handler.type) ) {
				event.handler = handler;
				event.data = handler.data;

				var ret = handler.apply( this, arguments );

				if ( ret !== undefined ) {
					event.result = ret;
					if ( ret === false ) {
						event.preventDefault();
						event.stopPropagation();
					}
				}

				if ( event.isImmediatePropagationStopped() ) {
					break;
				}

			}
		}

		return event.result;

 

 可以看到

event.data = handler.data。

 

事件方法返回 false 相当于:

event.preventDefault();
event.stopPropagation();

 

//-------------------

 未完~

 

分享到:
评论

相关推荐

    jQuery源码详细分析中文注释

    《jQuery源码详细分析中文注释》是一份深入解析jQuery库源码的宝贵资源,它为开发者提供了理解这个广泛使用的JavaScript库内部工作机制的机会。jQuery以其简洁的API和强大的功能深受前端开发者的喜爱,但其背后的...

    jquery api, jquery ui api, jquery源码分析

    深入理解jQuery源码有助于开发者更好地利用其API并优化性能。主要关注点包括: 1. **选择器引擎(Sizzle)**:jQuery 使用Sizzle作为其选择器引擎,它是如何快速高效地解析CSS选择器并找到匹配元素的。 2. **链式...

    韩顺平 jquery 源码 传智播客

    《深入解析jQuery源码——基于韩顺平讲解》 jQuery,作为一款广泛应用于Web开发的JavaScript库,以其简洁、易用的API深受开发者喜爱。它极大地简化了DOM操作、事件处理、动画效果以及Ajax交互等任务。韩顺平老师的...

    jquery源码和API

    二、jQuery源码解析 jQuery的源码组织结构清晰,分为核心(core)、选择器(selector)、DOM操作(manipulation)、事件(events)、遍历(traversing)、效果(effects)等多个模块。源码中大量使用了闭包和原型链...

    jquery-1.11源码

    《深入解析jQuery 1.11源码》 jQuery,作为JavaScript库的杰出代表,以其简洁易用的API,极大地简化了DOM操作、事件处理、动画制作和Ajax交互。...对jQuery源码的探索,是提升JavaScript开发能力的重要步骤。

    佟刚jQuery源码含资源及PPT

    接下来,关于jQuery源码解析,学习者可能会接触到jQuery的模块化设计、事件委托、选择器引擎Sizzle、以及性能优化策略。jQuery采用模块化设计,如将选择器、遍历、事件等功能拆分成独立模块,便于维护和扩展。Sizzle...

    锋利的JQuery源码

    《锋利的JQuery源码》是一本深入解析jQuery库的专著,旨在帮助开发者更好地理解和运用这个强大的JavaScript库。这本书包含多个章节,每个章节都深入探讨了jQuery的核心功能和扩展应用,通过阅读源码,我们可以了解到...

    锋利的jQuery源码

    《锋利的jQuery源码》是一本深入剖析jQuery核心机制的书籍,对于任何希望深入了解这一广泛使用的JavaScript库的开发者来说,都是不可或缺的资源。jQuery以其简洁的语法、强大的功能和良好的浏览器兼容性,成为了Web...

    Day09【JQuery】源码及综合案例

    【jQuery 源码解析与综合案例】 jQuery 是一个广泛使用的 JavaScript 库,它极大地简化了 JavaScript 的 DOM 操作、事件处理、动画效果以及Ajax交互。这个“Day09【JQuery】源码及综合案例”主题将深入探讨 jQuery ...

    jquery源码讲解

    《jQuery源码详解》 jQuery,作为一款广泛应用于前端开发的JavaScript库,以其简洁的API、强大的功能和良好的浏览器兼容性赢得了开发者们的喜爱。本文将深入解析jQuery的源码,帮助你更好地理解其内部机制,从而...

    Jquery源码

    《jQuery源码解析:Web前端开发的基石》 jQuery,作为Web前端开发中广泛使用的JavaScript库,其简洁的API和强大的功能使得它成为了开发者们的首选工具。本文将深入探讨jQuery的源码,揭示其背后的机制,帮助你更好...

    妙味课堂--jQuery源码

    《jQuery源码解析:深入理解JavaScript库的精髓》 jQuery,作为一款广泛应用于Web开发的JavaScript库,以其简洁的API和强大的功能深受开发者喜爱。它极大地简化了DOM操作、事件处理、动画效果以及Ajax交互等任务,...

    jquery源码

    《jQuery源码解析与经典应用探析》 jQuery,作为一款强大的JavaScript库,自诞生以来就深受开发者喜爱,它的简洁语法、高效的DOM操作以及丰富的插件生态,使得网页动态效果的实现变得轻而易举。本文将深入探讨...

    jQuery3.3.1源码

    本篇将聚焦于jQuery 3.3.1这一经典版本,通过对其源码的解析,帮助开发者深入理解其背后的实现原理和设计思路。 首先,jQuery的核心理念是“Write Less, Do More”,它通过封装了一系列简洁的API,使得开发者可以更...

    Jquery 1.2.6源码分析 pdf

    Jquery1.26版本的源码分析,pdf格式文档,相当清淅的书籍,前端开发者必看的类库书籍,虽然网上有很多jQuery的实例,但真正深入分析介绍jQuery源码细节的文档太少了,所以本篇文稿希望是你期待的,文档内容包括:...

    jQuery 源码分析

    三、源码解析 1. **选择器引擎Sizzle**:Sizzle采用ACSS(抽象CSS选择器)语法,通过编译CSS选择器为可执行的JavaScript代码,提高了性能。例如,`$("div.myClass")`会被转化为遍历DOM的高效函数。 2. **DOM操作的...

    jQuery_3.2.1_源码及手册.chm

    下面将对jQuery 3.2.1的核心知识点进行深入解析。 一、jQuery简介 jQuery是一个轻量级的JavaScript库,它简化了HTML文档遍历、事件处理、动画制作以及Ajax交互。它的设计目标是使JavaScript编程变得更加简单,尤其...

    京东商城商品详情页面网页源码解析(静动态结合,包含CSS、HTML、Javascript、jQuery)

    这个资源,"京东商城商品详情页面网页源码解析",为初学者提供了一个实践平台,涵盖了前端开发中的关键技术:CSS、HTML、JavaScript和jQuery。让我们深入探讨这些知识点。 首先,HTML(HyperText Markup Language)...

    jquery.accordion源码

    《jQuery Accordion 源码解析》 在Web开发领域,jQuery库以其简洁的API和强大的功能深受开发者喜爱。其中,jQuery UI中的Accordion组件是一种常见的交互元素,它将多个内容区块折叠在一个有限的空间内,用户可以...

Global site tag (gtag.js) - Google Analytics