这一篇,我们要看 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源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码...
jQuery源码分析系列.pdf
jQuery源码解读,jQuery源码解读
jQuery源码分析-插件
1、基本选择器 2、层次选择器 3、过滤选择器 4、表单选择器 $("#myELement") 选择id值等于myElement的元素,id值不能重复在文档中只能有一个id值是myElement所以得到的是唯一的元素 $("div") 选择所有的div标签元素...
Jquery源码分析 清晰 Jquery源码分析 清晰 Jquery源码分析 清晰 Jquery源码分析 清晰
jquery源码分析,包括入口技术,选择器入口,以及在选择器使用的时候需要注意的优化思路
jQuery以极其优雅的设计风格倾倒众生,不得不承认它的设计风格是众多JS库首屈一指的典范,难怪许多前端工程师坚守阵营,但是在使用jQuery的同时很少有人会去思索它背后的神秘色彩,这也是我撰写这系列文章的根本原因...
jquery1.2.6源码分析,其中里面含有源码详细的中文注释
锋利的jQuery 源码锋利的jQuery 源码锋利的jQuery 源码锋利的jQuery 源码锋利的jQuery 源码锋利的jQuery 源码
Head First jquery源码
jquery最新源码jquery最新源码jquery最新源码
jquery源码 带格式 读了一遍挺不错了 在这分享了
本视频为jquery视频,适用于会点js的人群,逐步分析了jquery源码
jQuery源码详细分析中文注释
jQuery学习资料 锋利的Jquery 源码 锋利的Jquery 源码 锋利的Jquery 源码
最新的开发版jQuery源码。jquery-1.8.0.js
Head First jQuery 中文版 源码