这一篇,我们要看 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库源码的宝贵资源,它为开发者提供了理解这个广泛使用的JavaScript库内部工作机制的机会。jQuery以其简洁的API和强大的功能深受前端开发者的喜爱,但其背后的...
深入理解jQuery源码有助于开发者更好地利用其API并优化性能。主要关注点包括: 1. **选择器引擎(Sizzle)**:jQuery 使用Sizzle作为其选择器引擎,它是如何快速高效地解析CSS选择器并找到匹配元素的。 2. **链式...
《深入解析jQuery源码——基于韩顺平讲解》 jQuery,作为一款广泛应用于Web开发的JavaScript库,以其简洁、易用的API深受开发者喜爱。它极大地简化了DOM操作、事件处理、动画效果以及Ajax交互等任务。韩顺平老师的...
二、jQuery源码解析 jQuery的源码组织结构清晰,分为核心(core)、选择器(selector)、DOM操作(manipulation)、事件(events)、遍历(traversing)、效果(effects)等多个模块。源码中大量使用了闭包和原型链...
《深入解析jQuery 1.11源码》 jQuery,作为JavaScript库的杰出代表,以其简洁易用的API,极大地简化了DOM操作、事件处理、动画制作和Ajax交互。...对jQuery源码的探索,是提升JavaScript开发能力的重要步骤。
接下来,关于jQuery源码解析,学习者可能会接触到jQuery的模块化设计、事件委托、选择器引擎Sizzle、以及性能优化策略。jQuery采用模块化设计,如将选择器、遍历、事件等功能拆分成独立模块,便于维护和扩展。Sizzle...
《锋利的JQuery源码》是一本深入解析jQuery库的专著,旨在帮助开发者更好地理解和运用这个强大的JavaScript库。这本书包含多个章节,每个章节都深入探讨了jQuery的核心功能和扩展应用,通过阅读源码,我们可以了解到...
《锋利的jQuery源码》是一本深入剖析jQuery核心机制的书籍,对于任何希望深入了解这一广泛使用的JavaScript库的开发者来说,都是不可或缺的资源。jQuery以其简洁的语法、强大的功能和良好的浏览器兼容性,成为了Web...
【jQuery 源码解析与综合案例】 jQuery 是一个广泛使用的 JavaScript 库,它极大地简化了 JavaScript 的 DOM 操作、事件处理、动画效果以及Ajax交互。这个“Day09【JQuery】源码及综合案例”主题将深入探讨 jQuery ...
《jQuery源码详解》 jQuery,作为一款广泛应用于前端开发的JavaScript库,以其简洁的API、强大的功能和良好的浏览器兼容性赢得了开发者们的喜爱。本文将深入解析jQuery的源码,帮助你更好地理解其内部机制,从而...
《jQuery源码解析:Web前端开发的基石》 jQuery,作为Web前端开发中广泛使用的JavaScript库,其简洁的API和强大的功能使得它成为了开发者们的首选工具。本文将深入探讨jQuery的源码,揭示其背后的机制,帮助你更好...
《jQuery源码解析:深入理解JavaScript库的精髓》 jQuery,作为一款广泛应用于Web开发的JavaScript库,以其简洁的API和强大的功能深受开发者喜爱。它极大地简化了DOM操作、事件处理、动画效果以及Ajax交互等任务,...
《jQuery源码解析与经典应用探析》 jQuery,作为一款强大的JavaScript库,自诞生以来就深受开发者喜爱,它的简洁语法、高效的DOM操作以及丰富的插件生态,使得网页动态效果的实现变得轻而易举。本文将深入探讨...
本篇将聚焦于jQuery 3.3.1这一经典版本,通过对其源码的解析,帮助开发者深入理解其背后的实现原理和设计思路。 首先,jQuery的核心理念是“Write Less, Do More”,它通过封装了一系列简洁的API,使得开发者可以更...
Jquery1.26版本的源码分析,pdf格式文档,相当清淅的书籍,前端开发者必看的类库书籍,虽然网上有很多jQuery的实例,但真正深入分析介绍jQuery源码细节的文档太少了,所以本篇文稿希望是你期待的,文档内容包括:...
三、源码解析 1. **选择器引擎Sizzle**:Sizzle采用ACSS(抽象CSS选择器)语法,通过编译CSS选择器为可执行的JavaScript代码,提高了性能。例如,`$("div.myClass")`会被转化为遍历DOM的高效函数。 2. **DOM操作的...
下面将对jQuery 3.2.1的核心知识点进行深入解析。 一、jQuery简介 jQuery是一个轻量级的JavaScript库,它简化了HTML文档遍历、事件处理、动画制作以及Ajax交互。它的设计目标是使JavaScript编程变得更加简单,尤其...
这个资源,"京东商城商品详情页面网页源码解析",为初学者提供了一个实践平台,涵盖了前端开发中的关键技术:CSS、HTML、JavaScript和jQuery。让我们深入探讨这些知识点。 首先,HTML(HyperText Markup Language)...
《jQuery Accordion 源码解析》 在Web开发领域,jQuery库以其简洁的API和强大的功能深受开发者喜爱。其中,jQuery UI中的Accordion组件是一种常见的交互元素,它将多个内容区块折叠在一个有限的空间内,用户可以...