`
jljlpch
  • 浏览: 324019 次
  • 性别: Icon_minigender_1
  • 来自: 南昌
社区版块
存档分类
最新评论

Event 系列: jquery event 源码

阅读更多
/*
 * author:prk
 * date:2008-08-17
 * comment:analyse of jquery event
 * 
 */
jQuery.event = {

	// add 事件到一个元素上。
	add : function(elem, types, handler, data) {
		if (elem.nodeType == 3 || elem.nodeType == 8)// 空白节点或注释
			return;

		// IE不能传入window,先复制一下。
		if (jQuery.browser.msie && elem.setInterval)
			elem = window;

		// 为handler分配一个全局唯一的Id
		if (!handler.guid)
			handler.guid = this.guid++;

		// 把data附到handler.data中
		if (data != undefined) {
			var fn = handler;
			handler = this.proxy(fn, function() {// 唯一Id,wrap原始handler Fn
						return fn.apply(this, arguments);
					});
			handler.data = data;
		}

		// 初始化元素的events。如果没有取到events中值,就初始化data: {}
		var events = jQuery.data(elem, "events")
				|| jQuery.data(elem, "events", {}),
		// 如果没有取到handle中值,就初始化data: function() {....}
		handle = jQuery.data(elem, "handle")
				|| jQuery.data(elem, "handle", function() {
					// 处理一个触发器的第二个事件和当page已经unload之后调用一个事件。
						if (typeof jQuery != "undefined"
								&& !jQuery.event.triggered)
							return jQuery.event.handle.apply(// arguments.callee.elem=handle.elem
									arguments.callee.elem, arguments);
					});
		// 增加elem做为handle属性,防止IE由于没有本地Event而内存泄露。
		handle.elem = elem;

		// 处理采用空格分隔多个事件名,如jQuery(...).bind("mouseover mouseout", fn);
		jQuery.each(types.split(/\s+/), function(index, type) {
			// 命名空间的事件,一般不会用到。
				var parts = type.split(".");
				type = parts[0];
				handler.type = parts[1];

				// 捆绑到本元素type事件的所有处理函数
				var handlers = events[type];

				if (!handlers) {// 没有找到处理函数列表就初始化事件队列
					handlers = events[type] = {};

					// 如果type不是ready,或ready的setup执行返回false
					if (!jQuery.event.special[type]
							|| jQuery.event.special[type].setup
									.call(elem, data) === false) {
						// 调用系统的事件函数来注册事件
						if (elem.addEventListener)// FF
							elem.addEventListener(type, handle, false);
						else if (elem.attachEvent)// IE
							elem.attachEvent("on" + type, handle);
					}
				}

				// 把处理器的id和handler形式属性对的形式保存在handlers列表中,
				// 也存在events[type][handler.guid]中。
				handlers[handler.guid] = handler;

				// 全局缓存这个事件的使用标识
				jQuery.event.global[type] = true;
			});

		// 防止IE内存泄露。
		elem = null;
	},

	guid : 1,
	global : {},

	// 从元素中remove一个事件
	remove : function(elem, types, handler) {
		if (elem.nodeType == 3 || elem.nodeType == 8)
			return;
		// 取出元素的events中Fn列表
		var events = jQuery.data(elem, "events"), ret, index;

		if (events) {
			// remove所有的该元素的事件 .是命名空间的处理
			if (types == undefined
					|| (typeof types == "string" && types.charAt(0) == "."))
				for (var type in events)
					this.remove(elem, type + (types || ""));
			else {
				// types, handler参数采用{type:xxx,handler:yyy}形式
				if (types.type) {
					handler = types.handler;
					types = types.type;
				}

				// 处理采用空格分隔多个事件名 jQuery(...).unbind("mouseover mouseout", fn);
				jQuery
						.each(types.split(/\s+/), function(index, type) {
							// 命名空间的事件,一般不会用到。
								var parts = type.split(".");
								type = parts[0];

								if (events[type]) {// 事件名找到
									if (handler)// handler传入,就remove事件名的这个处理函数
										delete events[type][handler.guid];//guid的作用
									else	// remove这个事件的所有处理函数,带有命名空间的处理
										for (handler in events[type])
											if (!parts[1]
													|| events[type][handler].type == parts[1])
												delete events[type][handler];

									// 如果没有该事件的处理函数存在,就remove事件名
									for (ret in events[type])// 看看有没有?
										break;
									if (!ret) {// 没有
										if (!jQuery.event.special[type]//不是ready
												|| jQuery.event.special[type].teardown
														.call(elem) === false) {// type不等于ready
											if (elem.removeEventListener)// 在浏览器中remove事件名
												elem.removeEventListener(type,
														jQuery.data(elem,
																"handle"),
														false);
											else if (elem.detachEvent)
												elem.detachEvent("on" + type,
														jQuery.data(elem,
																"handle"));
										}
										ret = null;
										delete events[type];// 在缓存中除去。
									}
								}
							});
			}

			// 不再使用,除去expando
			for (ret in events)
				break;
			if (!ret) {
				var handle = jQuery.data(elem, "handle");
				if (handle)
					handle.elem = null;
				jQuery.removeData(elem, "events");
				jQuery.removeData(elem, "handle");
			}
		}
	},

	trigger : function(type, data, elem, donative, extra) {
		data = jQuery.makeArray(data);

		if (type.indexOf("!") >= 0) {// 支持!的not的操作如!click,除click之后的所有
			type = type.slice(0, -1);// 除最后一个字符?
			var exclusive = true;
		}

		if (!elem) {// 处理全局的fire事件
			if (this.global[type])
				jQuery.each(jQuery.cache, function() {
					// 从cache中找到所有注册该事件的元素,触发改事件的处理函数
						if (this.events && this.events[type])
							jQuery.event.trigger(type, data, this.handle.elem);
					});
		} else {// 处理单个元素事件的fire事件
			if (elem.nodeType == 3 || elem.nodeType == 8)
				return undefined;

			var val, ret, fn = jQuery.isFunction(elem[type] || null),
			// 我们是否要提交一个伪造的事件?
			event = !data[0] || !data[0].preventDefault;

			// 构建伪造的事件。
			if (event) {
				data.unshift( {//存到数组中的第一个
					type : type,
					target : elem,
					preventDefault : function() {
					},
					stopPropagation : function() {
					},
					timeStamp : now()
				});
				data[0][expando] = true; // 不需要修正伪造事件
			}

			//防止事件名出错
			data[0].type = type;
			if (exclusive)
				data[0].exclusive = true;

			// 触发事件
			var handle = jQuery.data(elem, "handle");
			if (handle)
				val = handle.apply(elem, data);

			// Handle triggering native .onfoo handlers (and on links since we
			// don't call .click() for links)
			//处理触发.onfoo这样的本地处理方法,但是是对于links 's .click()不触发
			if ((!fn || (jQuery.nodeName(elem, 'a') && type == "click"))
					&& elem["on" + type]&& elem["on" + type].apply(elem, data) === false)
				val = false;

			// Extra functions don't get the custom event object
			if (event)
				data.shift();

			// 处理触发extra事件
			if (extra && jQuery.isFunction(extra)) {
		        //执行extra,同时把结果存到data中。
				ret = extra.apply(elem, val == null ? data : data.concat(val));
				// if anything is returned, give it precedence and have it
				// overwrite the previous value
				if (ret !== undefined)
					val = ret;
			}

			// 触发本地事件
			if (fn && donative !== false && val !== false
					&& !(jQuery.nodeName(elem, 'a') && type == "click")) {
				this.triggered = true;
				try {
					elem[type]();
					//对于一些hidden的元素,IE会报错
				} catch (e) {
				}
			}

			this.triggered = false;
		}

		return val;
	},

	handle : function(event) {
		// 返回 undefined or false
		var val, ret, namespace, all, handlers;

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

		// 命名空间处理
		namespace = event.type.split(".");
		event.type = namespace[0];
		namespace = namespace[1];
		// all = true 表明任何 handler
		all = !namespace && !event.exclusive;
		// 找到元素的events中缓存的事件名的处理函数列表
		handlers = (jQuery.data(this, "events") || {})[event.type];

		for (var j in handlers) {// 每个处理函数执行
			var handler = handlers[j];

			// Filter the functions by class
			if (all || handler.type == namespace) {
				// 传入引用,为了之后删除它们
				event.handler = handler;
				event.data = handler.data;

				ret = handler.apply(this, arguments);// 执行事件处理函数

				if (val !== false)
					val = ret;// 只要有一个处理函数返回false,本函数就返回false.

				if (ret === false) {// 不执行浏览器默认的动作
					event.preventDefault();
					event.stopPropagation();
				}
			}
		}

		return val;
	},

	props : "altKey attrChange attrName bubbles button cancelable charCode clientX "
			+ "clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode "
			+ "metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX "
			+ "screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which"
					.split(" "),

	//对事件进行包裹。
	fix : function(event) {
		if (event[expando] == true)//表明事件已经包裹过
			return event;

		//保存原始event,同时clone一个。
		var originalEvent = event;
		event = {
			originalEvent : originalEvent
		};

		for (var i = this.props.length, prop;i;) {
			prop = this.props[--i];
			event[prop] = originalEvent[prop];
		}
		
		event[expando] = true;
		
		//加上preventDefault and stopPropagation,在clone不会运行
		event.preventDefault = function() {
			// 在原始事件上运行
			if (originalEvent.preventDefault)
				originalEvent.preventDefault();
			
			originalEvent.returnValue = false;
		};
		event.stopPropagation = function() {
			// 在原始事件上运行
			if (originalEvent.stopPropagation)
				originalEvent.stopPropagation();
			
			originalEvent.cancelBubble = true;
		};

		// 修正 timeStamp
		event.timeStamp = event.timeStamp || now();

		// 修正target
		if (!event.target)
			event.target = event.srcElement || document; 			
		if (event.target.nodeType == 3)//文本节点是父节点。
			event.target = event.target.parentNode;

		// relatedTarget
		if (!event.relatedTarget && event.fromElement)
			event.relatedTarget = event.fromElement == event.target
					? event.toElement
					: event.fromElement;

		// Calculate pageX/Y if missing and clientX/Y available
		if (event.pageX == null && event.clientX != null) {
			var doc = document.documentElement, body = document.body;
			event.pageX = event.clientX
					+ (doc && doc.scrollLeft || body && body.scrollLeft || 0)
					- (doc.clientLeft || 0);
			event.pageY = event.clientY
					+ (doc && doc.scrollTop || body && body.scrollTop || 0)
					- (doc.clientTop || 0);
		}

		// Add which for key events
		if (!event.which
				&& ((event.charCode || event.charCode === 0)
						? event.charCode
						: event.keyCode))
			event.which = event.charCode || event.keyCode;

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if (!event.metaKey && event.ctrlKey)
			event.metaKey = event.ctrlKey;

		// Add which for click: 1 == left; 2 == middle; 3 == right
		// Note: button is not normalized, so don't use it
		if (!event.which && event.button)
			event.which = (event.button & 1 ? 1 : (event.button & 2
					? 3
					: (event.button & 4 ? 2 : 0)));

		return event;
	},

	proxy : function(fn, proxy) {
		// 作用就是分配全局guid.
		proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
		return proxy;
	},

	special : {
		ready : {
			// Make sure the ready event is setup
			setup : bindReady,
			teardown : function() {
			}
		}
	}
};

if (!jQuery.browser.msie) {
	// Checks if an event happened on an element within another element
	// Used in jQuery.event.special.mouseenter and mouseleave handlers
	var withinElement = function(event) {
		// Check if mouse(over|out) are still within the same parent element
		var parent = event.relatedTarget;
		// Traverse up the tree
		while (parent && parent != this)
			try {
				parent = parent.parentNode;
			} catch (e) {
				parent = this;
			}

		if (parent != this) {
			// set the correct event type
			event.type = event.data;
			// handle event if we actually just moused on to a non sub-element
			jQuery.event.handle.apply(this, arguments);
		}
	};

	jQuery.each( {
		mouseover : 'mouseenter',
		mouseout : 'mouseleave'
	}, function(orig, fix) {
		jQuery.event.special[fix] = {
			setup : function() {
				jQuery.event.add(this, orig, withinElement, fix);
			},
			teardown : function() {
				jQuery.event.remove(this, orig, withinElement);
			}
		};
	});
}

jQuery.fn.extend( {
	bind : function(type, data, fn) {
		return type == "unload" ? this.one(type, data, fn) : this
				.each(function() {// fn || data, fn && data实现了data参数可有可无
					jQuery.event.add(this, type, fn || data, fn && data);
				});
	},

	    // 为每一个匹配元素的特定事件(像click)绑定一个一次性的事件处理函数。
		// 在每个对象上,这个事件处理函数只会被执行一次。其他规则与bind()函数相同。
		// 这个事件处理函数会接收到一个事件对象,可以通过它来阻止(浏览器)默认的行为。
		// 如果既想取消默认的行为,又想阻止事件起泡,这个事件处理函数必须返回false。
		one : function(type, data, fn) {
			var one = jQuery.event.proxy(fn || data, function(event) {
				jQuery(this).unbind(event, one);
				return (fn || data).apply(this, arguments);// this-->当前的元素
				});
			return this.each(function() {
				jQuery.event.add(this, type, one, fn && data);
			});
		},

		// bind()的反向操作,从每一个匹配的元素中删除绑定的事件。
		// 如果没有参数,则删除所有绑定的事件。
		// 你可以将你用bind()注册的自定义事件取消绑定。
		// I如果提供了事件类型作为参数,则只删除该类型的绑定事件。
		// 如果把在绑定时传递的处理函数作为第二个参数,则只有这个特定的事件处理函数会被删除。
		unbind : function(type, fn) {
			return this.each(function() {
				jQuery.event.remove(this, type, fn);
			});
		},

	
		trigger : function(type, data, fn) {
			return this.each(function() {
				jQuery.event.trigger(type, data, this, true, fn);
			});
		},
       	//这个特别的方法将会触发指定的事件类型上所有绑定的处理函数。但不会执行浏览器默认动作.
		triggerHandler : function(type, data, fn) {
			return this[0]
					&& jQuery.event.trigger(type, data, this[0], false, fn);
		},
		
		//每次点击后依次调用函数。
		toggle : function(fn) {		
			var args = arguments, i = 1;
			
			while (i < args.length)//每个函数分配GUID
				jQuery.event.proxy(fn, args[i++]);

			return this.click(jQuery.event
					.proxy(fn, function(event) {//分配GUID					
							this.lastToggle = (this.lastToggle || 0) % i;//上一个函数							
							event.preventDefault();//阻止缺省动作
							//执行参数中的第几个函数,apply可以采用array-like的参数
							//With apply, you can use an array literal, 
							//for example, fun.apply(this, [name, value]),
							//or an Array object, for example, fun.apply(this, new Array(name, value)). 
							return args[this.lastToggle++].apply(this,
									arguments) || false;
						}));
		},
        
		//一个模仿悬停事件(鼠标移动到一个对象上面及移出这个对象)的方法。
		//这是一个自定义的方法,它为频繁使用的任务提供了一种“保持在其中”的状态。
        //当鼠标移动到一个匹配的元素上面时,会触发指定的第一个函数。当鼠标移出这个元素时,
		//会触发指定的第二个函数。而且,会伴随着对鼠标是否仍然处在特定元素中的检测(例如,处在div中的图像),
        //如果是,则会继续保持“悬停”状态,而不触发移出事件(修正了使用mouseout事件的一个常见错误)。
		hover : function(fnOver, fnOut) {
			return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
		},
        
		//dom ready时执行 fn
		ready : function(fn) {			
			bindReady();//注册监听			
			if (jQuery.isReady)//ready就运行				
				fn.call(document, jQuery);			
			else
				// 增加这个函数到queue中。可见支持无数的ready的调用。
				jQuery.readyList.push(function() {
					return fn.call(this, jQuery);
				});

			return this;
		}
	});

jQuery.extend( {
	isReady : false,
	readyList : [],
	// Handle when the DOM is ready
		ready : function() {			
			if (!jQuery.isReady) {		
				jQuery.isReady = true;
				
				if (jQuery.readyList) {					
					jQuery.each(jQuery.readyList, function() {
						this.call(document);
					});				
					jQuery.readyList = null;
				}
				
				jQuery(document).triggerHandler("ready");
			}
		}
	});

var readyBound = false;

function bindReady() {
	if (readyBound)
		return;
	readyBound = true;

	// Mozilla, Opera, webkit nightlies 支持DOMContentLoaded事件	
	if (document.addEventListener && !jQuery.browser.opera)
		//当DOMContentLoaded事件触发时就运行jQuery.ready
		document.addEventListener("DOMContentLoaded", jQuery.ready, false);

	//IE或不是frame的window
	if (jQuery.browser.msie && window == top)
		(function() {
			if (jQuery.isReady)
				return;
			try {
				// 在ondocumentready之前,一直都会抛出异常				
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left");
			} catch (error) {
				//一直运行bindReady()(=arguments.callee)
				setTimeout(arguments.callee, 0);
				return;
			}			
			jQuery.ready();//documentready就运行jQuery.ready
		})();

	if (jQuery.browser.opera)
		document.addEventListener("DOMContentLoaded", function() {
			if (jQuery.isReady)
				return;
				//只有styleSheets完全enable时,才是完全的load,其实还有pic
			for (var i = 0;i < document.styleSheets.length; i++)
				if (document.styleSheets[i].disabled) {//通过styleSheets来判断
					setTimeout(arguments.callee, 0);
					return;
				}			
				jQuery.ready();
			}, false);

	if (jQuery.browser.safari) {
		var numStyles;
		(function() {
			if (jQuery.isReady)
				return;
				//首先得得到readyState=loaded或=complete
			if (document.readyState != "loaded"
					&& document.readyState != "complete") {
				setTimeout(arguments.callee, 0);
				return;
			}
			//取得style的length,比较它们之间的长度,看看是不是完成loaded
			if (numStyles === undefined)
				numStyles = jQuery("style, link[rel=stylesheet]").length;
			if (document.styleSheets.length != numStyles) {
				setTimeout(arguments.callee, 0);
				return;
			}			
			jQuery.ready();
		})();
	}

	//最后只能依赖于window.load.
	jQuery.event.add(window, "load", jQuery.ready);
}

//为jquery对象增加常用的事件方法
jQuery
		.each(
				("blur,focus,load,resize,scroll,unload,click,dblclick,"
						+ "mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + "submit,keydown,keypress,keyup,error")
						.split(","), function(i, name) {					
				jQuery.fn[name] = function(fn) {
					return fn ? this.bind(name, fn) : this.trigger(name);
				};
			});

// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover in other browsers
// Window isn't included so as not to unbind existing unload events
jQuery(window).bind('unload', function() {
	for (var id in jQuery.cache)
		// Skip the window
		if (id != 1 && jQuery.cache[id].handle)
			jQuery.event.remove(jQuery.cache[id].handle.elem);
});

 

分享到:
评论

相关推荐

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

    2. **DOM 操作(DOM Manipulation)**:jQuery 提供了一系列方法用于创建、修改和操作DOM元素,如 `append()` 在元素末尾添加内容,`prepend()` 在元素开头添加内容,`html()` 和 `text()` 用于设置或获取元素的HTML...

    jQuery源码分析之Event事件分析

    对于事件的操作无非是addEvent,fireEvent,removeEvent这三个事 件方法。... Jquery提供了一个 event的包裹,这个相对于其它的lib提供的有点简单,但是足够使用。 代码如下: //对事件进行包裹。 fix : function(e

    Jquery几个实例源码

    2. **事件处理(Event Handling)**:jQuery提供了方便的方式来绑定和触发DOM事件。例如,`$("button").click(function() {...})`用于当按钮被点击时执行特定函数。 3. **DOM操作(DOM Manipulation)**:jQuery...

    jquery源码

    3. **DOM操作(DOM Manipulation)**:jQuery提供了一系列方便的DOM操作方法,如`append()`用于在元素内部追加内容,`remove()`用于删除元素,`clone()`用于复制元素等。 4. **事件处理(Events)**:jQuery简化了...

    jQuery3.3.1源码

    首先,jQuery的核心理念是“Write Less, Do More”,它通过封装了一系列简洁的API,使得开发者可以更高效地进行页面操作。在jQuery 3.3.1版本中,我们可以看到这一理念的体现。 1. **选择器(Selectors)**:jQuery...

    jQuery源码分析系列

    jQuery源码分析系列涉及了对jQuery库内部实现的详细解读,jQuery作为前端开发中最常用的JavaScript库之一,它简化了DOM操作、事件处理、动画效果和AJAX交互等操作。通过深入分析jQuery的源码,开发者可以学习到先进...

    jquery框架及源码

    4. **事件处理(Event Handling)**: jQuery简化了事件绑定和解绑,如`click()`、`hover()`和`live()`,并且提供了一致的跨浏览器事件处理。 5. **动画(Animation)**: jQuery的`fadeIn()`, `slideUp()`, `animate...

    jQuery从菜鸟到忍者书籍源码

    这本书籍的源码提供了丰富的实例和练习,使读者能够通过实际操作来加深对jQuery的理解。以下将详细介绍jQuery库的核心概念、功能及其在实际开发中的应用。 jQuery是一个广泛使用的JavaScript库,它的主要目标是简化...

    JQuery.js 源码(1.1 和2.x)

    3. **事件处理**:JQuery提供了一套简洁的API来绑定和解绑事件,如`$(selector).on('event', handler)`和`$(selector).off('event')`。此外,还有事件冒泡处理和事件委托的概念。 4. **链式调用**:JQuery对象的...

    锋利的JQuery源码

    3. **事件处理(Event Handling)**:jQuery简化了事件绑定和解绑的过程,`on()`和`off()`方法使得事件处理更加灵活。通过源码,我们可以理解事件委托机制以及事件冒泡的处理方式。 4. **动画效果(Animation)**:...

    《锋利的jQuery》实例源码

    1. **jQuery选择器**:jQuery提供了一系列高效的选择器,如ID选择器(`#id`)、类选择器(`.class`)、元素选择器(`element`)等,以及组合选择器( `,` 和 `:` ),例如`$("#myID .myClass")`用于选取ID为`myID`内...

    Jquery 权威指南配套源码

    3. **事件处理**:jQuery的事件处理非常灵活,如`click()`、`mouseover()`、`mouseout()`等,源码中会展示如何绑定和解绑事件,以及使用`event.preventDefault()`和`event.stopPropagation()`控制事件行为。...

    jquery源码和API

    jQuery的核心理念是“Write Less, Do More”,它通过封装了一系列高效的操作DOM、处理事件、创建动画的函数,使得开发者可以用更少的代码实现更多的功能。这主要体现在以下几个方面: 1. **选择器(Selectors)**:...

    jQuery源码分析(1.7)

    ### jQuery源码分析关键知识点详解 #### 一、前言 在深入了解jQuery源码之前,有必要先简要介绍一下jQuery的基本情况及其对JavaScript编程领域的重要意义。jQuery作为一个轻量级、功能丰富的JavaScript库,在Web...

    JQuery第二版的源码

    《jQuery第二版源码解析》 jQuery,作为一款广受欢迎的JavaScript库,为开发者提供了简单易用的API,使得DOM操作、事件处理、动画效果以及AJAX交互等任务变得轻而易举。当我们深入到jQuery的源码,不仅可以理解其...

    锋利的jQuery源码

    8. **源码结构**:了解jQuery的模块化设计,如`data()`, `queue()`, `event()`, `css()`等模块,以及它们之间的关系。 通过阅读《锋利的jQuery源码》,开发者不仅可以掌握jQuery的基本用法,还能深入理解其内部机制...

    锋利的JQuery第二版的源码

    3. **事件处理(Event Handling)**:jQuery提供了简洁的事件绑定方式,如`$(selector).click(function(){...})`用于绑定点击事件,`$(document).ready(function(){...})`确保在DOM加载完成后执行代码。 4. **动画...

    超实用的jquery代码段 源码

    这本书的源码提供了丰富的实践示例,下面我们将深入探讨jQuery的核心知识点。 1. **选择器**:jQuery的选择器是其强大之处之一,它基于CSS选择器,允许开发者快速准确地选取页面上的元素。例如,`$("#id")`选择ID为...

    jquery未压缩源码下载

    本资源提供了多个版本的jQuery未压缩源码下载,包括jQuery 1.7.2、jQuery 1.8.3、jQuery 2.0.3、jQuery 1.4.2、jQuery 1.10.2和jQuery 1.9.1。这些版本涵盖了jQuery发展中的不同时期,适合开发者根据项目需求选择...

Global site tag (gtag.js) - Google Analytics