You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */jQuery.event={add: function(elem,types,handler,data,selector){varelemData,eventHandle,events,t,tns,type,namespaces,handleObj,handleObjIn,quick,handlers,special;// 不为某些不支持的元素增加响应事件,参数不完整也不增加事件if(elem.nodeType===3||elem.nodeType===8||!types||!handler||!(elemData=jQuery._data(elem))){return;}// Caller can pass in an object of custom data in lieu of the handler// 处理一种特殊情况的监听函数// 它本身是一个自定义监听对象, 用两个变量来标示一下if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;}// handler没有guid就增加一个if(!handler.guid){handler.guid=jQuery.guid++;}// 取出或初始化dom元素的 events 数据缓存结构events=elemData.events;if(!events){elemData.events=events={};}// 取出或初始化dom元素的主监听函数 handleeventHandle=elemData.handle;if(!eventHandle){// 看得出来这个 主监听函数其实是一个 简单的对jQuery.event.dispatch的封装elemData.handle=eventHandle=function(e){// Discard the second event of a jQuery.event.trigger() and// when an event is called after a page has unloadedreturntypeofjQuery!=="undefined"&&(!e||jQuery.event.triggered!==e.type) ?
jQuery.event.dispatch.apply(eventHandle.elem,arguments) :
undefined;};// Add elem as a property of the handle fn to prevent a memory leak with IE non-native eventseventHandle.elem=elem;}// 处理多个事件类型 空格 隔开的绑定情况types=jQuery.trim(hoverHack(types)).split(" ");for(t=0;t<types.length;t++){tns=rtypenamespace.exec(types[t])||[];type=tns[1];namespaces=(tns[2]||"").split(".").sort();// If event changes its type, use the special event handlers for the changed typespecial=jQuery.event.special[type]||{};// If selector defined, determine special event api type, otherwise given typetype=(selector ? special.delegateType : special.bindType)||type;// Update special based on newly reset typespecial=jQuery.event.special[type]||{};// 把监听函数封装成了监听对象handleObj=jQuery.extend({type: type,origType: tns[1],data: data,handler: handler,guid: handler.guid,selector: selector,quick: quickParse(selector),namespace: namespaces.join(".")},handleObjIn);// 如果当前 dom数据缓存中没有对应的 事件类型,则初始化handlers=events[type];if(!handlers){handlers=events[type]=[];handlers.delegateCount=0;// 当没有特殊情况的时候,设置当前type事件的监听函数为 主监听函数handleif(!special.setup||special.setup.call(elem,data,namespaces,eventHandle)===false){// Bind the global event handler to the elementif(elem.addEventListener){elem.addEventListener(type,eventHandle,false);}elseif(elem.attachEvent){elem.attachEvent("on"+type,eventHandle);}}}if(special.add){special.add.call(elem,handleObj);if(!handleObj.handler.guid){handleObj.handler.guid=handler.guid;}}// 把真正的 监听函数 压入队列之中if(selector){handlers.splice(handlers.delegateCount++,0,handleObj);}else{handlers.push(handleObj);}// Keep track of which events have ever been used, for event optimizationjQuery.event.global[type]=true;}// Nullify elem to prevent memory leaks in IEelem=null;},global: {},// Detach an event or set of events from an elementremove: function(elem,types,handler,selector,mappedTypes){varelemData=jQuery.hasData(elem)&&jQuery._data(elem),t,tns,type,origType,namespaces,origCount,j,events,special,handle,eventType,handleObj;// 当前dom元素上没有任何关联的缓存数据,也就没有任何缓存事件, 那么直接returnif(!elemData||!(events=elemData.events)){return;}// Once for each type.namespace in types; type may be omitted// 拆解types, 因为支持命名空间和一次移除多个事件类型types=jQuery.trim(hoverHack(types||"")).split(" ");for(t=0;t<types.length;t++){// 拆解命名空间tns=rtypenamespace.exec(types[t])||[];type=origType=tns[1];namespaces=tns[2];// Unbind all events (on this namespace, if provided) for the element// 如果没有type 则表示需要移除所有事件类型if(!type){for(typeinevents){jQuery.event.remove(elem,type+types[t],handler,selector,true);}continue;}special=jQuery.event.special[type]||{};type=(selector? special.delegateType : special.bindType)||type;eventType=events[type]||[];origCount=eventType.length;namespaces=namespaces ? newRegExp("(^|\\.)"+namespaces.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)") : null;// Remove matching events// 循环匹配找哪个事件应该被删除for(j=0;j<eventType.length;j++){handleObj=eventType[j];if((mappedTypes||origType===handleObj.origType)&&(!handler||handler.guid===handleObj.guid)&&(!namespaces||namespaces.test(handleObj.namespace))&&(!selector||selector===handleObj.selector||selector==="**"&&handleObj.selector)){eventType.splice(j--,1);if(handleObj.selector){eventType.delegateCount--;}if(special.remove){special.remove.call(elem,handleObj);}}}// Remove generic event handler if we removed something and no more handlers exist// (avoids potential for endless recursion during removal of special event handlers)// 如果监听对象数组唯恐,则移除主监听函数if(eventType.length===0&&origCount!==eventType.length){if(!special.teardown||special.teardown.call(elem,namespaces)===false){jQuery.removeEvent(elem,type,elemData.handle);}deleteevents[type];}}// 如果没有任何事件了,那么直接删除缓存数据// Remove the expando if it's no longer usedif(jQuery.isEmptyObject(events)){handle=elemData.handle;if(handle){handle.elem=null;}// removeData also checks for emptiness and clears the expando if empty// so use it instead of deletejQuery.removeData(elem,["events","handle"],true);}},// Events that are safe to short-circuit if no handlers are attached.// Native DOM events should not be added, they may have inline handlers.customEvent: {"getData": true,"setData": true,"changeData": true},trigger: function(event,data,elem,onlyHandlers){// Don't do events on text and comment nodes// 过滤不正确的参数情况if(elem&&(elem.nodeType===3||elem.nodeType===8)){return;}// Event object or event typevartype=event.type||event,namespaces=[],cache,exclusive,i,cur,old,ontype,special,handle,eventPath,bubbleType;// focus/blur morphs to focusin/out; ensure we're not firing them right now// 如果正在处罚focus\blur事件默认行为,浏览器应该自动触发focusin\focusout// 这里先过滤掉 待会再统一补齐if(rfocusMorph.test(type+jQuery.event.triggered)){return;}// 修正参数 命名空间和!的传入if(type.indexOf("!")>=0){// Exclusive events trigger only for the exact event (no namespaces)type=type.slice(0,-1);exclusive=true;}if(type.indexOf(".")>=0){// Namespaced trigger; create a regexp to match event type in handle()namespaces=type.split(".");type=namespaces.shift();namespaces.sort();}// 阻止两类不对的触发事件if((!elem||jQuery.event.customEvent[type])&&!jQuery.event.global[type]){// No jQuery handlers for this event type, and it can't have inline handlersreturn;}// 修正为jquery.Event对象// Caller can pass in an Event, Object, or just an event type stringevent=typeofevent==="object" ?
// jQuery.Event objectevent[jQuery.expando] ? event :
// Object literalnewjQuery.Event(type,event) :
// Just the event type (string)newjQuery.Event(type);event.type=type;event.isTrigger=true;event.exclusive=exclusive;event.namespace=namespaces.join(".");event.namespace_re=event.namespace? newRegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.)?")+"(\\.|$)") : null;ontype=type.indexOf(":")<0 ? "on"+type : "";// Handle a global trigger// 如果没有elem标示触发所有同类事件// 只能循环缓存数据对象,逐个触发if(!elem){// TODO: Stop taunting the data cache; remove global events and always attach to documentcache=jQuery.cache;for(iincache){if(cache[i].events&&cache[i].events[type]){jQuery.event.trigger(event,data,cache[i].handle.elem,true);}}return;}// 继续休整几个参数// Clean up the event in case it is being reusedevent.result=undefined;if(!event.target){event.target=elem;}// Clone any incoming data and prepend the event, creating the handler arg listdata=data!=null ? jQuery.makeArray(data) : [];data.unshift(event);// Allow special events to draw outside the linesspecial=jQuery.event.special[type]||{};if(special.trigger&&special.trigger.apply(elem,data)===false){return;}// Determine event propagation path in advance, per W3C events spec (#9951)// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)// 因为模拟触发事件, 是需要模拟冒泡过程的,但是jQuery这个不会有自动的冒泡// 冒泡也比较容易模拟, 就一直找父元素就好了,父元素在查看有没有缓存数据eventPath=[[elem,special.bindType||type]];if(!onlyHandlers&&!special.noBubble&&!jQuery.isWindow(elem)){bubbleType=special.delegateType||type;cur=rfocusMorph.test(bubbleType+type) ? elem : elem.parentNode;old=null;for(;cur;cur=cur.parentNode){eventPath.push([cur,bubbleType]);old=cur;}// Only add window if we got to document (e.g., not plain obj or detached DOM)if(old&&old===elem.ownerDocument){eventPath.push([old.defaultView||old.parentWindow||window,bubbleType]);}}// 利用刚刚构建好的冒泡路径 再逐一的触发元素上面的绑定事件 如果有的话,并且不能是阻止冒泡了的// Fire handlers on the event pathfor(i=0;i<eventPath.length&&!event.isPropagationStopped();i++){cur=eventPath[i][0];event.type=eventPath[i][1];handle=(jQuery._data(cur,"events")||{})[event.type]&&jQuery._data(cur,"handle");if(handle){handle.apply(cur,data);}// Note that this is a bare JS function and not a jQuery handlerhandle=ontype&&cur[ontype];if(handle&&jQuery.acceptData(cur)&&handle.apply(cur,data)===false){event.preventDefault();}}event.type=type;// If nobody prevented the default action, do it now// 如果没有阻止默认行为,则最后执行默认行为 // 默认行为都是几个比较特殊的if(!onlyHandlers&&!event.isDefaultPrevented()){if((!special._default||special._default.apply(elem.ownerDocument,data)===false)&&!(type==="click"&&jQuery.nodeName(elem,"a"))&&jQuery.acceptData(elem)){// Call a native DOM method on the target with the same name name as the event.// Can't use an .isFunction() check here because IE6/7 fails that test.// Don't do default actions on window, that's where global variables be (#6170)// IE<9 dies on focus/blur to hidden element (#1486)if(ontype&&elem[type]&&((type!=="focus"&&type!=="blur")||event.target.offsetWidth!==0)&&!jQuery.isWindow(elem)){// Don't re-trigger an onFOO event when we call its FOO() methodold=elem[ontype];if(old){elem[ontype]=null;}// Prevent re-triggering of the same event, since we already bubbled it abovejQuery.event.triggered=type;elem[type]();jQuery.event.triggered=undefined;if(old){elem[ontype]=old;}}}}returnevent.result;},dispatch: function(event){// Make a writable jQuery.Event from the native event object// 包装一个jQuery.event, 如果已经是jQuery.event则不需要包装event=jQuery.event.fix(event||window.event);// 拿到当前事件类型对应的监听对象数组varhandlers=((jQuery._data(this,"events")||{})[event.type]||[]),delegateCount=handlers.delegateCount,args=[].slice.call(arguments,0),run_all=!event.exclusive&&!event.namespace,handlerQueue=[],i,j,cur,jqcur,ret,selMatch,matched,matches,handleObj,sel,related;// Use the fix-ed jQuery.Event rather than the (read-only) native eventargs[0]=event;event.delegateTarget=this;// Determine handlers that should run if there are delegated events// Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861)if(delegateCount&&!event.target.disabled&&!(event.button&&event.type==="click")){// Pregenerate a single jQuery object for reuse with .is()jqcur=jQuery(this);jqcur.context=this.ownerDocument||this;for(cur=event.target;cur!=this;cur=cur.parentNode||this){selMatch={};matches=[];jqcur[0]=cur;for(i=0;i<delegateCount;i++){handleObj=handlers[i];sel=handleObj.selector;if(selMatch[sel]===undefined){selMatch[sel]=(handleObj.quick ? quickIs(cur,handleObj.quick) : jqcur.is(sel));}if(selMatch[sel]){matches.push(handleObj);}}if(matches.length){handlerQueue.push({elem: cur,matches: matches});}}}// Add the remaining (directly-bound) handlersif(handlers.length>delegateCount){handlerQueue.push({elem: this,matches: handlers.slice(delegateCount)});}// Run delegates first; they may want to stop propagation beneath usfor(i=0;i<handlerQueue.length&&!event.isPropagationStopped();i++){matched=handlerQueue[i];event.currentTarget=matched.elem;for(j=0;j<matched.matches.length&&!event.isImmediatePropagationStopped();j++){handleObj=matched.matches[j];// Triggered event must either 1) be non-exclusive and have no namespace, or// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).if(run_all||(!event.namespace&&!handleObj.namespace)||event.namespace_re&&event.namespace_re.test(handleObj.namespace)){event.data=handleObj.data;event.handleObj=handleObj;ret=((jQuery.event.special[handleObj.origType]||{}).handle||handleObj.handler).apply(matched.elem,args);if(ret!==undefined){event.result=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}}}returnevent.result;},// Includes some event props shared by KeyEvent and MouseEvent// *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks: {},keyHooks: {props: "char charCode key keyCode".split(" "),filter: function(event,original){// 没有which,则依次想办法拿到一个值if(event.which==null){event.which=original.charCode!=null ? original.charCode : original.keyCode;}returnevent;}},mouseHooks: {props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter: function(event,original){vareventDoc,doc,body,button=original.button,fromElement=original.fromElement;// Calculate pageX/Y if missing and clientX/Y availableif(event.pageX==null&&original.clientX!=null){eventDoc=event.target.ownerDocument||document;doc=eventDoc.documentElement;body=eventDoc.body;event.pageX=original.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc&&doc.clientLeft||body&&body.clientLeft||0);event.pageY=original.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc&&doc.clientTop||body&&body.clientTop||0);}// Add relatedTarget, if necessaryif(!event.relatedTarget&&fromElement){event.relatedTarget=fromElement===event.target ? original.toElement : fromElement;}// Add which for click: 1 === left; 2 === middle; 3 === right// Note: button is not normalized, so don't use itif(!event.which&&button!==undefined){event.which=(button&1 ? 1 : (button&2 ? 3 : (button&4 ? 2 : 0)));}returnevent;}},// 用于讲原生事件对象封装为jQuery事件对象fix: function(event){// 如果有标示表明已经是jQuery事件对象,没必要继续封装了if(event[jQuery.expando]){returnevent;}// Create a writable copy of the event object and normalize some propertiesvari,prop,originalEvent=event,// fixHook 要不然就是鼠标事件mouseHooks 要不然就是 键盘事件keyHooksfixHook=jQuery.event.fixHooks[event.type]||{},copy=fixHook.props ? this.props.concat(fixHook.props) : this.props;event=jQuery.Event(originalEvent);// 根据事件类型,将一些原生事件的属性赋值给jQuery事件for(i=copy.length;i;){prop=copy[--i];event[prop]=originalEvent[prop];}// Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)// event target 可能在某些版本有bug, 他还有另外一个地方可以查到就是event.srcElementif(!event.target){event.target=originalEvent.srcElement||document;}// Target should not be a text node (#504, Safari)// 事件的target不应该是一个文本节点, 应该是dom 节点if(event.target.nodeType===3){event.target=event.target.parentNode;}// For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)// metaKey 表示事件触发的时候,win/command键是否被按下。 如果没有的话就用ctrl键状态代替if(event.metaKey===undefined){event.metaKey=event.ctrlKey;}// 这个ffixHook.filter可以看前面keyHooks和mouseHooks,其实就是继续修正某些转有bugreturnfixHook.filter? fixHook.filter(event,originalEvent) : event;},special: {ready: {// Make sure the ready event is setupsetup: jQuery.bindReady},load: {// Prevent triggered image.load events from bubbling to window.loadnoBubble: true},focus: {delegateType: "focusin"},blur: {delegateType: "focusout"},beforeunload: {setup: function(data,namespaces,eventHandle){// We only want to do this special case on windowsif(jQuery.isWindow(this)){this.onbeforeunload=eventHandle;}},teardown: function(namespaces,eventHandle){if(this.onbeforeunload===eventHandle){this.onbeforeunload=null;}}}},// 模拟一个事件的调用 或者 冒泡过程simulate: function(type,elem,event,bubble){// Piggyback on a donor event to simulate a different one.// Fake originalEvent to avoid donor's stopPropagation, but if the// simulated event prevents default then we do the same on the donor.vare=jQuery.extend(newjQuery.Event(),event,{type: type,isSimulated: true,originalEvent: {}});if(bubble){jQuery.event.trigger(e,null,elem);}else{jQuery.event.dispatch.call(elem,e);}if(e.isDefaultPrevented()){event.preventDefault();}}};// Some plugins are using, but it's undocumented/deprecated and will be removed.// The 1.7 special event interface should provide all the hooks needed now.jQuery.event.handle=jQuery.event.dispatch;jQuery.removeEvent=document.removeEventListener ?
function(elem,type,handle){if(elem.removeEventListener){elem.removeEventListener(type,handle,false);}} :
function(elem,type,handle){if(elem.detachEvent){elem.detachEvent("on"+type,handle);}};jQuery.Event=function(src,props){// Allow instantiation without the 'new' keyword// 这里可以省略new 一种新的写法if(!(thisinstanceofjQuery.Event)){returnnewjQuery.Event(src,props);}// Event object// src有type属性表示传入的是原生事件类型if(src&&src.type){// 原生事件类型的话 做备份this.originalEvent=src;this.type=src.type;// isDefaultPrevented用来判断当前jQuery时间对象上是否调用过方法preventDefaultthis.isDefaultPrevented=(src.defaultPrevented||src.returnValue===false||src.getPreventDefault&&src.getPreventDefault()) ? returnTrue : returnFalse;// Event type}else{this.type=src;}// Put explicitly provided properties onto the event objectif(props){jQuery.extend(this,props);}// 给新的事件增加一个时间戳this.timeStamp=src&&src.timeStamp||jQuery.now();// 增加一个标记this[jQuery.expando]=true;};functionreturnFalse(){returnfalse;}functionreturnTrue(){returntrue;}// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.htmljQuery.Event.prototype={// 阻止默认行为preventDefault: function(){// 标示preventDefault函数被调用过了this.isDefaultPrevented=returnTrue;// 如果当前不是浏览器原生事件,那么不会有任何默认行为,直接returnvare=this.originalEvent;if(!e){return;}// ie9以下和标准的兼容处理,阻止默认行为// if preventDefault exists run it on the original eventif(e.preventDefault){e.preventDefault();// otherwise set the returnValue property of the original event to false (IE)}else{e.returnValue=false;}},// 阻止冒泡stopPropagation: function(){// 表示调用过阻止冒泡了this.isPropagationStopped=returnTrue;// 非原生事件不会有冒泡行为// 自定义事件不会进行传播vare=this.originalEvent;if(!e){return;}// if stopPropagation exists run it on the original eventif(e.stopPropagation){e.stopPropagation();}// otherwise set the cancelBubble property of the original event to true (IE)e.cancelBubble=true;},// 模拟了原生的stopImmediatePropagation方法// stopImmediatePropagation的意思就是// 当一个元素某个事件有多个监听器时,触发后会按照先后顺序依次执行,但是如果设置了阻止冒泡,则祖先元素不会触发同类事件,但是原本的多个监听器还是会依次执行完毕。// 但是如果又一个监听器执行了 event.stopImmediatePropagation, 那么后续的监听器将不会继续执行stopImmediatePropagation: function(){this.isImmediatePropagationStopped=returnTrue;this.stopPropagation();},isDefaultPrevented: returnFalse,isPropagationStopped: returnFalse,isImmediatePropagationStopped: returnFalse};// Create mouseenter/leave events using mouseover/out and event-time checksjQuery.each({mouseenter: "mouseover",mouseleave: "mouseout"},function(orig,fix){jQuery.event.special[orig]={delegateType: fix,bindType: fix,handle: function(event){vartarget=this,related=event.relatedTarget,handleObj=event.handleObj,selector=handleObj.selector,ret;// For mousenter/leave call the handler if related is outside the target.// NB: No relatedTarget if the mouse left/entered the browser windowif(!related||(related!==target&&!jQuery.contains(target,related))){event.type=handleObj.origType;ret=handleObj.handler.apply(this,arguments);event.type=fix;}returnret;}};});// IE submit delegationif(!jQuery.support.submitBubbles){jQuery.event.special.submit={setup: function(){// Only need this for delegated form submit eventsif(jQuery.nodeName(this,"form")){returnfalse;}// Lazy-add a submit handler when a descendant form may potentially be submittedjQuery.event.add(this,"click._submit keypress._submit",function(e){// Node name check avoids a VML-related crash in IE (#9807)varelem=e.target,form=jQuery.nodeName(elem,"input")||jQuery.nodeName(elem,"button") ? elem.form : undefined;if(form&&!form._submit_attached){jQuery.event.add(form,"submit._submit",function(event){// If form was submitted by the user, bubble the event up the treeif(this.parentNode&&!event.isTrigger){jQuery.event.simulate("submit",this.parentNode,event,true);}});form._submit_attached=true;}});// return undefined since we don't need an event listener},teardown: function(){// Only need this for delegated form submit eventsif(jQuery.nodeName(this,"form")){returnfalse;}// Remove delegated handlers; cleanData eventually reaps submit handlers attached abovejQuery.event.remove(this,"._submit");}};}// IE change delegation and checkbox/radio fixif(!jQuery.support.changeBubbles){jQuery.event.special.change={setup: function(){if(rformElems.test(this.nodeName)){// IE doesn't fire change on a check/radio until blur; trigger it on click// after a propertychange. Eat the blur-change in special.change.handle.// This still fires onchange a second time for check/radio after blur.if(this.type==="checkbox"||this.type==="radio"){jQuery.event.add(this,"propertychange._change",function(event){if(event.originalEvent.propertyName==="checked"){this._just_changed=true;}});jQuery.event.add(this,"click._change",function(event){if(this._just_changed&&!event.isTrigger){this._just_changed=false;jQuery.event.simulate("change",this,event,true);}});}returnfalse;}// Delegated event; lazy-add a change handler on descendant inputsjQuery.event.add(this,"beforeactivate._change",function(e){varelem=e.target;if(rformElems.test(elem.nodeName)&&!elem._change_attached){jQuery.event.add(elem,"change._change",function(event){if(this.parentNode&&!event.isSimulated&&!event.isTrigger){jQuery.event.simulate("change",this.parentNode,event,true);}});elem._change_attached=true;}});},handle: function(event){varelem=event.target;// Swallow native change events from checkbox/radio, we already triggered them aboveif(this!==elem||event.isSimulated||event.isTrigger||(elem.type!=="radio"&&elem.type!=="checkbox")){returnevent.handleObj.handler.apply(this,arguments);}},teardown: function(){jQuery.event.remove(this,"._change");returnrformElems.test(this.nodeName);}};}// Create "bubbling" focus and blur eventsif(!jQuery.support.focusinBubbles){jQuery.each({focus: "focusin",blur: "focusout"},function(orig,fix){// Attach a single capturing handler while someone wants focusin/focusoutvarattaches=0,handler=function(event){jQuery.event.simulate(fix,event.target,jQuery.event.fix(event),true);};jQuery.event.special[fix]={setup: function(){if(attaches++===0){document.addEventListener(orig,handler,true);}},teardown: function(){if(--attaches===0){document.removeEventListener(orig,handler,true);}}};});}jQuery.fn.extend({on: function(types,selector,data,fn,/*INTERNAL*/one){varorigFn,type;// 修正参数 start// on(types, fn) // on(types, selector, fn)// on(types, data, fn)// Types can be a map of types/handlersif(typeoftypes==="object"){// ( types-Object, selector, data )if(typeofselector!=="string"){// ( types-Object, data )data=selector;selector=undefined;}for(typeintypes){this.on(type,selector,data,types[type],one);}returnthis;}if(data==null&&fn==null){// ( types, fn )fn=selector;data=selector=undefined;}elseif(fn==null){if(typeofselector==="string"){// ( types, selector, fn )fn=data;data=undefined;}else{// ( types, data, fn )fn=data;data=selector;selector=undefined;}}if(fn===false){fn=returnFalse;}elseif(!fn){returnthis;}// 修正参数 end// 如果有one是1, 表示监听函数只能被执行一次if(one===1){// 重新设置fn为一个 执行时先移除监听事件再执行函数的方法origFn=fn;fn=function(event){jQuery().off(event);returnorigFn.apply(this,arguments);};// 设置两者为相同的guid,方便移除的时候操作fn.guid=origFn.guid||(origFn.guid=jQuery.guid++);}returnthis.each(function(){jQuery.event.add(this,types,fn,data,selector);});},one: function(types,selector,data,fn){returnthis.on.call(this,types,selector,data,fn,1);},off: function(types,selector,fn){// 有这两个属性表示 types是一个jQuery.event对象// 内部移除当前正在执行的事件if(types&&types.preventDefault&&types.handleObj){// ( event ) dispatched jQuery.EventvarhandleObj=types.handleObj;jQuery(types.delegateTarget).off(handleObj.namespace? handleObj.type+"."+handleObj.namespace : handleObj.type,handleObj.selector,handleObj.handler);returnthis;}// 如果是一个数组则循环调用off, 移除相对应的监听事件if(typeoftypes==="object"){// ( types-object [, selector] )for(vartypeintypes){this.off(type,selector,types[type]);}returnthis;}// 没有selector, 修正参数if(selector===false||typeofselector==="function"){// ( types [, fn] )fn=selector;selector=undefined;}if(fn===false){fn=returnFalse;}// 执行真正的监听事件移除的方法returnthis.each(function(){jQuery.event.remove(this,types,fn,selector);});},bind: function(types,data,fn){returnthis.on(types,null,data,fn);},unbind: function(types,fn){returnthis.off(types,null,fn);},live: function(types,data,fn){jQuery(this.context).on(types,this.selector,data,fn);returnthis;},die: function(types,fn){jQuery(this.context).off(types,this.selector||"**",fn);returnthis;},delegate: function(selector,types,data,fn){returnthis.on(types,selector,data,fn);},undelegate: function(selector,types,fn){// ( namespace ) or ( selector, types [, fn] )returnarguments.length==1? this.off(selector,"**") : this.off(types,selector,fn);},// 主动去触发事件trigger: function(type,data){returnthis.each(function(){jQuery.event.trigger(type,data,this);});},triggerHandler: function(type,data){if(this[0]){returnjQuery.event.trigger(type,data,this[0],true);}},// 一个快速设置点击事件 响应多个对调函数的方法, // 其实所有函数被作为一个闭包加到缓存数据中,他们可以一起被移除toggle: function(fn){// Save reference to arguments for access in closurevarargs=arguments,guid=fn.guid||jQuery.guid++,i=0,toggler=function(event){// Figure out which function to executevarlastToggle=(jQuery._data(this,"lastToggle"+fn.guid)||0)%i;jQuery._data(this,"lastToggle"+fn.guid,lastToggle+1);// Make sure that clicks stopevent.preventDefault();// and execute the functionreturnargs[lastToggle].apply(this,arguments)||false;};// link all the functions, so any of them can unbind this click handlertoggler.guid=guid;while(i<args.length){args[i++].guid=guid;}returnthis.click(toggler);},hover: function(fnOver,fnOut){returnthis.mouseenter(fnOver).mouseleave(fnOut||fnOver);}});jQuery.each(("blur focus focusin focusout load resize scroll unload click dblclick "+"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave "+"change select submit keydown keypress keyup error contextmenu").split(" "),function(i,name){// Handle event bindingjQuery.fn[name]=function(data,fn){if(fn==null){fn=data;data=null;}returnarguments.length>0 ?
this.on(name,null,data,fn) :
this.trigger(name);};if(jQuery.attrFn){jQuery.attrFn[name]=true;}if(rkeyEvent.test(name)){jQuery.event.fixHooks[name]=jQuery.event.keyHooks;}if(rmouseEvent.test(name)){jQuery.event.fixHooks[name]=jQuery.event.mouseHooks;}});
ready部分
// 这是一个ready的主入口函数,jQuqey.fn={ready: function(fn){// Attach the listeners// 初始化rady、构建readyList、增加监听函数DOMCONTENTLOADEDjQuery.bindReady();// Add the callbackreadyList.add(fn);returnthis;},}// 这是初始化DOMContentLoaded的代码,下面一段会用到if(document.addEventListener){DOMContentLoaded=function(){document.removeEventListener("DOMContentLoaded",DOMContentLoaded,false);jQuery.ready();};}elseif(document.attachEvent){DOMContentLoaded=function(){// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).if(document.readyState==="complete"){document.detachEvent("onreadystatechange",DOMContentLoaded);jQuery.ready();}};}jQuery.extend({// 传入一个真值, 就会让readyWait这个flag增加1holdReady: function(hold){if(hold){jQuery.readyWait++;}else{jQuery.ready(true);}},// 由于这里面要判断是不是isReady才会执行,所以当已经执行过ready之后再使用on.(ready)添加的方法不会被执行,但是直接添加到队列的会被执行,刚才讲过队列的属性了// Handle when the DOM is readyready: function(wait){// 只有是hlodReady那边调用wait才有可能为true, 代表着readyWait减少1// Either a released hold or an DOMready/load event and not yet readyif((wait===true&&!--jQuery.readyWait)||(wait!==true&&!jQuery.isReady)){// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).if(!document.body){returnsetTimeout(jQuery.ready,1);}// Remember that the DOM is ready// 更改ready标记jQuery.isReady=true;// If a normal DOM Ready event fired, decrement, and wait if need be// 不满足条件证明ready事件被延迟了,需要继续等待ready事件被减少,就是holdReady(false)if(wait!==true&&--jQuery.readyWait>0){return;}// 执行队列// If there are functions bound, to executereadyList.fireWith(document,[jQuery]);// 绑定ready事件// Trigger any bound ready eventsif(jQuery.fn.trigger){jQuery(document).trigger("ready").off("ready");}}},bindReady: function(){// readyList第一次定义为undefinedif(readyList){return;}// readyList定义为一个值执行一次的队列 memory表示之后添加的函数立即被调用,也就是说$.ready()之后还可以继续执行readyList=jQuery.Callbacks("once memory");// jquery加载的有点慢,这时候整个文档都已经加载完毕了,直接立即执行ready吧// Catch cases where $(document).ready() is called after the// browser event has already occurred.if(document.readyState==="complete"){// Handle it asynchronously to allow scripts the opportunity to delay readyreturnsetTimeout(jQuery.ready,1);}// Mozilla, Opera and webkit nightlies currently support this eventif(document.addEventListener){// Use the handy event callbackdocument.addEventListener("DOMContentLoaded",DOMContentLoaded,false);// A fallback to window.onload, that will always workwindow.addEventListener("load",jQuery.ready,false);// If IE event model is used}elseif(document.attachEvent){// ensure firing before onload,// maybe late but safe also for iframesdocument.attachEvent("onreadystatechange",DOMContentLoaded);// A fallback to window.onload, that will always workwindow.attachEvent("onload",jQuery.ready);// If IE and not a frame// continually check to see if the document is readyvartoplevel=false;// 如果食顶层 不是iframe 可以通过doScrollCheck来测试是不是文档已经准备好try{toplevel=window.frameElement==null;}catch(e){}if(document.documentElement.doScroll&&toplevel){doScrollCheck();}}},})// 在ie下 document.documentElement.doScroll("left") 方法不报错,就证明页面dom加载好了functiondoScrollCheck(){if(jQuery.isReady){return;}try{// If IE is used, use the trick by Diego Perini// http://javascript.nwbox.com/IEContentLoaded/document.documentElement.doScroll("left");}catch(e){setTimeout(doScrollCheck,1);return;}// and execute any waiting functionsjQuery.ready();}
The text was updated successfully, but these errors were encountered:
jQuery事件系统
jQuery的事件系统以之前分析过的数据缓存系统为基础。之前也分析过,数据缓存部分可以为每个DOM元素、甚至普通的JS对象都增加缓存属性,缓存属性中只有一个区域存放自定义的数据,而系统内置的缺占据了很多缓存区域。 事件系统就是一个占据区域的大户。
当我们把事件添加到某个DOM元素上面的时候,例如
click
事件,真正被添加到系统addeventListener
的不是我们传入的那个函数,而是jQuery帮我们代理的一个handler
函数。而且我们在使用过程中,也不再使用系统的event
对象,而是使用jQuery封装过的jQuery.event
对象,这个对象抹平了很多浏览器之间的差异。当事件触发的时候,我们可以看到我们注册到dom元素的那个事件其实是一个
dispatch
事件---也就是做事件分发的工作,它的任务就是在缓存数据中,找到对应类型的缓存事件数组,然后依次执行。这样看来,dom元素上面,同一类型的事件只会被绑定一个函数,也就是dispatch
方法,当事件被触发,就去数据里面找相对应的回调函数队列。事件主动触发又做了哪些工作呢。事件在主动触发的时候,jQuery还模拟了事件冒泡的过程,毕竟这在浏览器中还是一个不小的需求,事件代理不就是那么完成的吗。事件冒泡思路并不难,把所有父元素一直记录下来组成冒泡路径,然后依次检测是不是有同类型的监听事件,有的话执行,没有的话也没办法喽。
事件移除这时候也就能大概想明白了,应该也不会是真正的移除某个dom监听,肯定也是移除的缓存数据中的那个监听函数,当然如果缓存对象为空了,可以相对应的移除掉某事件的真正绑定的
dispatch
函数,甚至整个dom对应的缓存数据。这里就有了一个问题,平时如果我们自定义事件,触发它是一件麻烦的事件,因为你要
new Event('custome'); dispatch('custome')
,但是在jQuerry这里没有这个概念了,因为事件不是真正绑定在DOM上面,而是放在了缓存数据里面,自定义的事件,你只能主动触发,主动触发的时候dom就只是一座桥梁,以寻找对应的缓存数据而已,找到数据 则就是纯js的函数执行问题了。在整个jQuery事件系统中, jqeury帮我们封装了一个jQuey.Event对象,这个对象磨平了很多浏览器之间的差异,也相当于做了一个统一规范,这样既帮助于内部个函数、接口直接的掉哟过,也有助于我们更好的拿到更统一的数据。
我们还可以发现
ready
方法竟然不在event部分代码里面,ready
又单独拎出来了。ready那边其实挺神奇的就是在监听了一个
DOMContentReady
事件,所以他才会比我们平时使用的load
事件快,因为load是要等各类资源加载完毕才会触发,如果是淘宝页面,那图片加载的酸爽。 ready部分的代码注释的相当完备,也比较简单。ready部分
The text was updated successfully, but these errors were encountered: