jQ的一個個版本事系統都在修正著bug和不斷優化, 而且看了事件系統對事件的相容更加熟悉, 更加了解jQ內部的事件機制。

  因為jQ對事件系統引入了事件名稱空間,事件的代理, 事件的手動觸發,事件描述等等各種概念, 對事件的可操控性大大增加, 這個也是庫存在的意義, 不是說只要處理addEventListener和attachEvent可以做到的;在大型的專案中事件系統也可以作為釋出者和派發者,對整個系統進行充分的解耦

  這些做為自己的筆記,一步一步走, 只是大概看了看, 還有不懂的地方, 最好的學習原始碼的方式不是看別人給我們說, 也不是看別人寫的書(參考是可以得), 自己動手是永恆不變的真理, 加油, 現在jQ的程式碼分析都爛大街了,估計就我自己看看或者說是複習複習了。。。。

    var rnamespaces = /\.(.*)$/,
rformElems = /^(?:textarea|input|select)$/i,
rperiod = /\./g,
rspace = / /g,
rescape = /[^\w\s.|`]/g,
fcleanup = function( nm ) {
return nm.replace(rescape, "\\$&");
},
eventKey = "events";
/*
知識點匹配需要轉義的字元;
rescape = /[^\w\s.|`]/g,
//為每一個需要轉義的字元新增\
nm.replace(rescape, "\\$&");
fcleanup("sdfsdfdsfds.s&**(((*)f\\")
"sdfsdfdsfds.s\&\*\*\(\(\(\*\)f\\"
*/
/*
* A number of helper functions used for managing events.
* Many of the ideas behind this code originated from
* Dean Edwards' addEvent library.
*/
jQuery.event = {
// Bind an event to an element
// Original by Dean Edwards
//所有繫結事件都是通過add這個方法繫結的;
//元素
//click mouseover 正常情況下; // 如果是代理的情況下; add會繫結兩次,第一次是繫結live事件,第二個是繫結click事件; 第一個點以後的是要匹配的元素選擇器(要把 ^替換成.);
//"live.click.div"
// "live.click.`hehe" ==>> click.`hehe;
// live.click.div`hehe ==>> "click.div`hehe";
//data沒毛用;
add: function( elem, types, handler, data ) {
//debugger;
//text節點和 comment節點全滾哇, 話說 屬性節點(nodeType === 2)的你可以繫結事件嗎? 是的,好像真的可以哇, 奇葩了;
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
// 跨iframe
if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
elem = window;
} //繫結一個空的事件
if ( handler === false ) {
handler = returnFalse;
} else if ( !handler ) {
// Fixes bug #7229. Fix recommended by jdalton
return;
}; var handleObjIn, handleObj; //根據傳進來的handler是否有handler屬性,然後設定handlerObjIn事件描述和,事件觸發要執行的函式handlerObjIn.handler;
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
}; // Make sure that the function being executed has a unique ID
//保證了唯一的事件, 後面可以根據這個唯一的id進行remove或者別的操作,比較方便;
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}; // Init the element's event structure
//這個獲取的是內部私用的快取儲存的資料;
var elemData = jQuery._data( elem ); // If no elemData is found then we must be trying to bind to one of the
// banned noData elements
// 沒有就不玩啦, 為什麼會沒有呢 ,因為noData的元素不能亂給事件哇, object[classId="/\d/mig"], applet, embed;
// 對啊embed不能繫結事件,只能通過innerHTML繫結事件, 以前碰到過這種情況;
if ( !elemData ) {
return;
};
//eventKey = "events" 上面定義了這個鳥東西;
var events = elemData[ eventKey ],
//eventHandle是為這個元素繫結的事件
eventHandle = elemData.handle; //正常的events應該是一個數組哇, 是function的情況應該特別對待;
if ( typeof events === "function" ) {
// On plain objects events is a fn that holds the the data
// which prevents this data from being JSON serialized
// the function does not need to be called, it just contains the data
eventHandle = events.handle;
events = events.events; } else if ( !events ) {
//處理非節點元素的事件繫結, 這個應該是為了擴張繫結事件到非節點元素上面;
if ( !elem.nodeType ) {
// On plain objects, create a fn that acts as the holder
// of the values to avoid JSON serialization of event data
elemData[ eventKey ] = elemData = function(){};
}; //新建一個事件儲存列表;
elemData.events = events = {};
}; //所有的事件都繫結同一個事件函式, 剩下的給event.handle處理不同的情況;
//使用這種方式對使用者的來說, 可配置性變好了, 比如
// 1 : 你可以讓事件按照順序執行(某些瀏覽器不按照順序來,因為事件執行時冒泡階段執行);
// 2 : 沒想出來;
if ( !eventHandle ) {
elemData.handle = eventHandle = function() {
// Handle the second event of a trigger and when
// an event is called after a page has unloaded
//jQuery.event.triggered預設是false的;
return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
jQuery.event.handle.apply( eventHandle.elem, arguments ) :
undefined;
};
}; // Add elem as a property of the handle function
// This is to prevent a memory leak with non-native events in IE.
// 為事件函式新增元素的引用; 阻止ie下的記憶體洩漏;
eventHandle.elem = elem; // Handle multiple events separated by a space
// jQuery(...).bind("mouseover mouseout", fn);
//開始了一大堆處理, 對繫結的事件進行c;
types = types.split(" "); var type, i = 0, namespaces; while ( (type = types[ i++ ]) ) {
//重新弄一個事件描述(引用);
handleObj = handleObjIn ?
jQuery.extend({}, handleObjIn) :
{ handler: handler, data: data }; // Namespaced event handlers
// 修復時間的名稱空間;
// 目測現在事件代理被弄成 live click^#div1^div的情況
if ( type.indexOf(".") > -1 ) {
namespaces = type.split(".");
type = namespaces.shift();
handleObj.namespace = namespaces.slice(0).sort().join(".");
} else {
namespaces = [];
handleObj.namespace = "";
}; //為事件描述新增事件型別
handleObj.type = type;
//為事件描述新增事件的guid, 這個handle是從bind那邊處理過的(處理了one,bind), 也可能從live那邊傳過來的;
if ( !handleObj.guid ) {
handleObj.guid = handler.guid;
} // Get the current list of functions bound to this event
// 建立或者獲取事件的佇列;
var handlers = events[ type ],
special = jQuery.event.special[ type ] || {}; // Init the event handler queue
if ( !handlers ) {
handlers = events[ type ] = []; // Check for a special event handler
// Only use addEventListener/attachEvent if the special
// events handler returns false
// 如果繫結的是beforeunload,就特殊對待,
// //如果繫結focusin或者foucuseout就轉化成使用fouse和blur,
// live或者是
// 是ready就繫結到document.ready
// 如果是mouseenter或者是mouseleave,就使用mouseout和mousein模擬;
// live只有add和remove,所以這個setup肯定不走, 直接走addEVentListener的繫結;
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
//如果是live就是綁定了自定義事件, 觸發的時候要注意一下;
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
} //使用事件代理的時候的卻有點繞, jQ高版本的話對事件代理進行了優化;; //live的時候,這裡又綁定了一次哦,只有live有add和remove;
if ( special.add ) {
//第一次add的是live的handlers,第二次add的才是真正的handlers;
//呼叫special的繫結方式進行繫結;
//這個繫結有重新迭代了一次$.event.add...所以要注意一下, 這一次的迭代才是真正繫結需要的事件
special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
};
};
//繫結事件的事件函式要做不同處理, 但是繫結的事件描述還是要根據事件的型別放到handlers裡面去; //所有的處理都是為了把handleObj放到handlers這個物件裡面;
// Add the function to the element's handler list
handlers.push( handleObj ); //優化;
// Keep track of which events have been used, for global triggering
jQuery.event.global[ type ] = true; /*handle結構是這樣的
$.cache = {
Number ElementGuid : {
string jQueryExpando : {
events : {
"click" : [function(){}, function(){}, function(){}, function(){}]
},
handle : function(){....}
}
}
}
*/
}; // Nullify elem to prevent memory leaks in IE
elem = null;
}, global: {}, // Detach an event or set of events from an element
//刪除事件目測應該和繫結差不多道理;
remove: function( elem, types, handler, pos ) {
// don't do events on text and comment nodes
// 依舊可以把事件繫結給屬性節點;
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}; if ( handler === false ) {
handler = returnFalse;
}; //
var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
events = elemData && elemData[ eventKey ]; //沒有事件列表還玩個毛;
if ( !elemData || !events ) {
return;
}; //這個和$.event.add一樣的,
if ( typeof events === "function" ) {
elemData = events;
events = events.events;
}; //$.event.remove({type : "click",handler : clickFn});
// types is actually an event object here
if ( types && types.type ) {
handler = types.handler;
types = types.type;
}; // Unbind all events for the element
// 沒有types的話, 就是移除所有的事件;
//型別是名稱空間的話
if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
types = types || ""; //迭代名稱空間的事件,一個個刪除;
for ( type in events ) {
jQuery.event.remove( elem, type + types );
};
//下面沒必要在走了;
return;
}; // Handle multiple events separated by a space
// jQuery(...).unbind("mouseover mouseout", fn);
types = types.split(" "); while ( (type = types[ i++ ]) ) {
origType = type;
handleObj = null;
all = type.indexOf(".") < 0;
namespaces = []; //all 指是或否是這個事件的全部名稱空間都要刪除;
if ( !all ) {
// Namespaced event handlers
namespaces = type.split(".");
type = namespaces.shift(); namespace = new RegExp("(^|\\.)" +
jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
}; eventType = events[ type ]; if ( !eventType ) {
continue;
}; if ( !handler ) {
for ( j = 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
//對這個事件描述物件進行判斷, 如果匹配到了這個名稱空間就把這個時間刪了;;
if ( all || namespace.test( handleObj.namespace ) ) {
jQuery.event.remove( elem, origType, handleObj.handler, j );
eventType.splice( j--, 1 );
}
} continue;
} special = jQuery.event.special[ type ] || {}; for ( j = pos || 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
//使用者也可以傳繫結的函式進來, 如果guid一樣就刪;
if ( handler.guid === handleObj.guid ) {
// remove the given handler for the given type
if ( all || namespace.test( handleObj.namespace ) ) {
if ( pos == null ) {
eventType.splice( j--, 1 );
}; //有remove的只有live有了;
if ( special.remove ) {
special.remove.call( elem, handleObj );
};
} if ( pos != null ) {
break;
}
}
} //如果某個事件的 事件列表刪除完了, 就把這個events【type】清空;
// remove generic event handler if no more handlers exist
if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
//mousein mouseout focusin fousout 對走這個;
if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}; ret = null;
delete events[ type ];
};
}; // Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
var handle = elemData.handle;
if ( handle ) {
handle.elem = null;
} delete elemData.events;
delete elemData.handle; if ( typeof elemData === "function" ) {
jQuery.removeData( elem, eventKey, true ); } else if ( jQuery.isEmptyObject( elemData ) ) {
jQuery.removeData( elem, undefined, true );
}
}
}, //trigger是給使用者用的;
// bubbling is internal
trigger: function( event, data, elem /*, bubbling */ ) {
// Event object or event type
var type = event.type || event,
bubbling = arguments[3]; //預設都是冒泡;
if ( !bubbling ) {
//自己新建一個event物件;
event = typeof event === "object" ?
// jQuery.Event object
event[ jQuery.expando ] ? event :
// Object literal
jQuery.extend( jQuery.Event(type), event ) :
// Just the event type (string)
jQuery.Event(type); // 有!的代表觸發的是自定義的屬性更改事件, 對使用者來說,作用不多,有點像IE的onpropertychange;
if ( type.indexOf("!") >= 0 ) {
event.type = type = type.slice(0, -1);
event.exclusive = true;
}; // Handle a global
if ( !elem ) {
// 如果你要執行對應type全部事件,那麼就要阻止預設事件
// 如果你不阻止冒泡的話會
// Don't bubble custom events when global (to avoid too much overhead)
// 這個event是假的,模擬出來的東東;
event.stopPropagation(); // Only trigger if we've ever bound an event for it
if ( jQuery.event.global[ type ] ) {
// XXX This code smells terrible. event.js should not be directly
// inspecting the data cache
jQuery.each( jQuery.cache, function() {
// internalKey variable is just used to make it easier to find
// and potentially change this stuff later; currently it just
// points to jQuery.expando
var internalKey = jQuery.expando,
internalCache = this[ internalKey ];
if ( internalCache && internalCache.events && internalCache.events[type] ) {
jQuery.event.trigger( event, data, internalCache.handle.elem );
}
});
//我不知道這裡為什麼不return掉;
}
} // Handle triggering a single element // don't do events on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
return undefined;
} // Clean up in case it is reused
event.result = undefined;
event.target = elem; // Clone the incoming data, if any
data = jQuery.makeArray( data );
data.unshift( event );
} //
event.currentTarget = elem; // Trigger the event, it is assumed that "handle" is a function
var handle = elem.nodeType ?
jQuery._data( elem, "handle" ) :
(jQuery._data( elem, eventKey ) || {}).handle; //這個就是手動觸發事件了哇, data裡面是有新建的event物件的;
if ( handle ) {
handle.apply( elem, data );
}; var parent = elem.parentNode || elem.ownerDocument; //手動觸發行內繫結的事件;
// Trigger an inline bound script
try {
if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
event.result = false;
event.preventDefault();
}
} // prevent IE from throwing an error for some elements with some event types, see #3533
} catch (inlineError) {} //我靠這個,又手動觸發了父級的對應事件,就是事件冒泡了 ,(jQ為什麼考慮這麼全面);
if ( !event.isPropagationStopped() && parent ) {
jQuery.event.trigger( event, data, parent, true ); //預設事件沒有被阻止的話;
} else if ( !event.isDefaultPrevented() ) {
var old,
target = event.target,
targetType = type.replace( rnamespaces, "" ),
isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
special = jQuery.event.special[ targetType ] || {}; if ( (!special._default || special._default.call( elem, event ) === false) &&
!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { try {
if ( target[ targetType ] ) {
// Make sure that we don't accidentally re-trigger the onFOO events
old = target[ "on" + targetType ]; if ( old ) {
target[ "on" + targetType ] = null;
} jQuery.event.triggered = true;
target[ targetType ]();
} // prevent IE from throwing an error for some elements with some event types, see #3533
} catch (triggerError) {} if ( old ) {
target[ "on" + targetType ] = old;
} jQuery.event.triggered = false;
}
}
}, //所繫結的事件, 這個方法的event是最初的事件物件;
handle: function( event ) {
var all, handlers, namespaces, namespace_re, events,
namespace_sort = [],
//除了event瀏覽器的呼叫這個事件以外, 使用者也可以模擬一個假的event,假的eventType等等,手動觸發哦;
args = jQuery.makeArray( arguments );
//對事件的event進行瀏覽器相容統一處理
event = args[0] = jQuery.event.fix( event || window.event );
// currentTarget指的是繫結事件的元素;,
// 如果是代理繫結的話, 那麼事件函式裡面的this不是繫結的元素, 使用者如果有需要的話通過currentTarget引用即可;
event.currentTarget = this; // Namespaced event handlers
//如果沒有名稱空間的話就是true;
/*比如 你通過
$("body").bind("click.nameSpace0",function(){console.log(1)}) 綁定了事件,
$("body").bind("click.nameSpace1",function(){console.log(1)})
當你左鍵點選body元素的時候 這兩個繫結的事件都會觸發;
但是你想手動觸發nameSpace0這個事件的話,你可以直接trigger("click.nameSpace0");
事件的名稱空間要看你怎麼用了, 不用也沒大問題, 主要是解耦了各個事件函式;
*/
all = event.type.indexOf(".") < 0 && !event.exclusive; //這個也只有使用者手動觸發的時候會走;
if ( !all ) {
namespaces = event.type.split(".");
//事件名和事件名稱空間拿出來;
event.type = namespaces.shift();
namespace_sort = namespaces.slice(0).sort();
//開頭或者是一個dot;
//結尾或者是一個dot;
namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
}; //別忘記了名稱空間和liveHanler是不一樣的,別犯迷糊, 一個是事件的分類,一個是在父級上面繫結事件;
event.namespace = event.namespace || namespace_sort.join("."); //獲取所有事件
// eventKey === "events";
events = jQuery._data(this, eventKey); //預設的events不會是function的, 這個是什麼情況,好像是jQuery.special.type[ name ]那裡面的事件;
if ( typeof events === "function" ) {
events = events.events;
}; //handler不是函式哦, 是所有有關這個事件的事件描述
//標準的事件描述物件 應該是這樣的:{data : guid : handle :function(){}, name : "xx", type : "click"}
//獲取對應的事件 比如 click 還是 mouseoout這樣的情況;
handlers = (events || {})[ event.type ]; //如果沒有繫結對應的事件就不走裡面, 作用1:優化, 2:避免裡面報錯;
if ( events && handlers ) {
// Clone the handlers to prevent manipulation
//複製一個事件描述物件;
handlers = handlers.slice(0); //迭代所有的事件;
for ( var j = 0, l = handlers.length; j < l; j++ ) {
var handleObj = handlers[ j ]; // Filter the functions by class
if ( all || namespace_re.test( handleObj.namespace ) ) {
// Pass in a reference to the handler function itself
// So that we can later remove it
//事件
event.handler = handleObj.handler;
//事件的資料;為事件新增data這個這麼重要嗎,還是我不會用;
event.data = handleObj.data;
//把handleObj事件描述物件掛到event事件物件上面
event.handleObj = handleObj;
//現在事件裡面的事件物件就有了handleObj事件描述物件這東西了;
//執行事件;
//預設的就一個event, 如果不是預設的就會把所有的引數重新傳進去;
//利用這一點,我們可以把自己定義個釋出者和訂閱者,而且引數自己填(event都是要的哇)
var ret = handleObj.handler.apply( this, args ); //進行而外的處理
if ( ret !== undefined ) {
//把資料儲存到event.result, 下次執行的話,可以呼叫event.result獲取上次事件儲存的值, 有用,HOW?
event.result = ret;
//對return false進行特殊的處理;
if ( ret === false ) {
event.preventDefault();
event.stopPropagation();
};
}; //不執行了哇, 跳出這個迴圈了;
if ( event.isImmediatePropagationStopped() ) {
break;
}
}
}
} return event.result;
}, props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), //複製一個事件物件, 統一事件物件的相容;
fix: function( event ) {
//如果已經經過jQ處理過的事件物件;
if ( event[ jQuery.expando ] ) {
return event;
} // store a copy of the original event object
// and "clone" to set read-only properties
var originalEvent = event;
event = jQuery.Event( originalEvent ); //根據原始的物件複製一個假的事件物件, 要複製的屬性分別是:
//altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which;
for ( var i = this.props.length, prop; i; ) {
prop = this.props[ --i ];
event[ prop ] = originalEvent[ prop ];
}; // Fix target property, if necessary
if ( !event.target ) {
//火狐下是srcElement ,chorme和ie都是target;
// Fixes #1925 where srcElement might not be defined either
event.target = event.srcElement || document;
}; // check if target is a textnode (safari)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
} // Add relatedTarget, if necessary
//修復IE下沒有relateTarget但是有fromeElement和toElement;
if ( !event.relatedTarget && event.fromElement ) {
event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
}; //這個也是IE的問題,沒有pageX和pageY;,根據clientX或者clientY加上介面滾動值在減去IE678下(Body,或者HTML標籤上)的2px問題;
// 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 && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
}; //DOM3規定使用whitch, 不用charCode也不用keyCode;
// Add which for key events
if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
event.which = event.charCode != null ? event.charCode : event.keyCode;
}; //蘋果系統的META鍵就是window中的CTRL鍵;
// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
if ( !event.metaKey && event.ctrlKey ) {
event.metaKey = event.ctrlKey;
}
//剛剛測試過了,筆記本上面的fn鍵即使按住了,事件物件 並沒有 按住fn鍵的屬性 顯示;
// Add which for click: 1 === left; 2 === middle; 3 === right
// Note: button is not normalized, so don't use it
// 保證了當前不是通過鍵盤的事件;
//保證了是button這個鍵存在值;
if ( !event.which && event.button !== undefined ) {
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
}; return event;
}, // Deprecated, use jQuery.guid instead
guid: 1E8, // Deprecated, use jQuery.proxy instead
proxy: jQuery.proxy, //這幾個事件繫結的時候要特殊對待, 移除繫結也要特殊對待;
special: { //當頁面載入完畢以後要初始化的幾個方法;
ready: {
// Make sure the ready event is setup
setup: jQuery.bindReady,
teardown: jQuery.noop
}, live: {
//事件代理是根據事件描述handleObj物件 , 重新繫結事件, 不過handle是liveHandler,這個很重要;
add: function( handleObj ) {
//這個就呼叫了add;
jQuery.event.add( this,
// click.^div^#div1^klass
liveConvert( handleObj.origType, handleObj.selector ),
//使用liveHandler作為事件物件;
jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
}, remove: function( handleObj ) {
jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
}
}, beforeunload: {
setup: function( data, namespaces, eventHandle ) {
// We only want to do this special case on windows
if ( jQuery.isWindow( this ) ) {
this.onbeforeunload = eventHandle;
}
}, teardown: function( namespaces, eventHandle ) {
if ( this.onbeforeunload === eventHandle ) {
this.onbeforeunload = null;
}
}
}
}
}; //第一次執行就把正確的函式賦值給物件屬性;
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 ) {
// Allow instantiation without the 'new' keyword
// if !(this instanceof jQuery.Event) 也行;
if ( !this.preventDefault ) {
return new jQuery.Event( src );
} // Event object
// 一般來說src是物件的話,應該是系統提供的事件物件;
if ( src && src.type ) {
this.originalEvent = src;
this.type = src.type; // Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; // Event type
} else {
this.type = src;
} // timeStamp is buggy for some events on Firefox(#3843)
// So we won't rely on the native value
this.timeStamp = jQuery.now(); // Mark it as fixed
this[ jQuery.expando ] = true;
}; function returnFalse() {
return false;
}
function returnTrue() {
return true;
} // 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.html
jQuery.Event.prototype = {
preventDefault: function() {
this.isDefaultPrevented = returnTrue; var e = this.originalEvent;
if ( !e ) {
return;
} // if preventDefault exists run it on the original event
if ( 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; var e = this.originalEvent;
if ( !e ) {
return;
}
// if stopPropagation exists run it on the original event
if ( e.stopPropagation ) {
e.stopPropagation();
}
// otherwise set the cancelBubble property of the original event to true (IE)
e.cancelBubble = true;
},
stopImmediatePropagation: function() {
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
},
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse
}; //模擬mouseenter和mouseleave;
// 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; // FiwithinElementrefox sometimes assigns relatedTarget a XUL element
// which we cannot access the parentNode property of
try {
// Traverse up the tree
while ( parent && parent !== this ) {
parent = parent.parentNode;
}; 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 );
} // assuming we've left the element since we most likely mousedover a xul element
} catch(e) { }
}, // In case of event delegation, we only need to rename the event.type,
// liveHandler will take care of the rest.
delegate = function( event ) {
event.type = event.data;
jQuery.event.handle.apply( this, arguments );
}; // Create mouseenter and mouseleave events
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout"
}, function( orig, fix ) {
jQuery.event.special[ orig ] = {
//setup就是繫結事件
setup: function( data ) {
jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
},
//teardown就是取消事件
teardown: function( data ) {
jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
}
};
}); // submit delegation
if ( !jQuery.support.submitBubbles ) { jQuery.event.special.submit = {
//繫結事件
setup: function( data, namespaces ) {
if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
jQuery.event.add(this, "click.specialSubmit", function( e ) {
var elem = e.target,
type = elem.type; if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
e.liveFired = undefined;
return trigger( "submit", this, arguments );
}
}); jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
var elem = e.target,
type = elem.type; if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
e.liveFired = undefined;
return trigger( "submit", this, arguments );
}
}); } else {
return false;
}
}, //取消事件;
teardown: function( namespaces ) {
jQuery.event.remove( this, ".specialSubmit" );
}
}; }
// setup和teardown這些東西自己新增擴充套件事件, 就是閉包閉包又是閉包,一層一層一層又是一層;
// change delegation, happens here so we have bind.
if ( !jQuery.support.changeBubbles ) { var changeFilters, getVal = function( elem ) {
var type = elem.type, val = elem.value; if ( type === "radio" || type === "checkbox" ) {
val = elem.checked; } else if ( type === "select-multiple" ) {
val = elem.selectedIndex > -1 ?
jQuery.map( elem.options, function( elem ) {
return elem.selected;
}).join("-") :
""; } else if ( elem.nodeName.toLowerCase() === "select" ) {
val = elem.selectedIndex;
} return val;
}, testChange = function testChange( e ) {
var elem = e.target, data, val; if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
return;
} data = jQuery._data( elem, "_change_data" );
val = getVal(elem); // the current data will be also retrieved by beforeactivate
if ( e.type !== "focusout" || elem.type !== "radio" ) {
jQuery._data( elem, "_change_data", val );
} if ( data === undefined || val === data ) {
return;
} if ( data != null || val ) {
e.type = "change";
e.liveFired = undefined;
return jQuery.event.trigger( e, arguments[1], elem );
}
}; jQuery.event.special.change = {
filters: {
focusout: testChange, beforedeactivate: testChange, click: function( e ) {
var elem = e.target, type = elem.type; if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
return testChange.call( this, e );
}
}, // Change has to be called before submit
// Keydown will be called before keypress, which is used in submit-event delegation
keydown: function( e ) {
var elem = e.target, type = elem.type; if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
(e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
type === "select-multiple" ) {
return testChange.call( this, e );
}
}, // Beforeactivate happens also before the previous element is blurred
// with this event you can't trigger a change event, but you can store
// information
beforeactivate: function( e ) {
var elem = e.target;
jQuery._data( elem, "_change_data", getVal(elem) );
}
}, //繫結事件
setup: function( data, namespaces ) {
if ( this.type === "file" ) {
return false;
} for ( var type in changeFilters ) {
jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
} return rformElems.test( this.nodeName );
}, //取消事件
teardown: function( namespaces ) {
jQuery.event.remove( this, ".specialChange" ); return rformElems.test( this.nodeName );
}
}; changeFilters = jQuery.event.special.change.filters; // Handle when the input is .focus()'d
changeFilters.focus = changeFilters.beforeactivate;
} function trigger( type, elem, args ) {
args[0].type = type;
return jQuery.event.handle.apply( elem, args );
} //修復瀏覽器fousein和fouseout支援;
// Create "bubbling" focus and blur events
if ( document.addEventListener ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
jQuery.event.special[ fix ] = {
setup: function() {
this.addEventListener( orig, handler, true );
},
teardown: function() {
this.removeEventListener( orig, handler, true );
}
}; function handler( e ) {
e = jQuery.event.fix( e );
e.type = fix;
return jQuery.event.handle.call( this, e );
}
});
} //這個是繼承到例項原型上面的程式碼;
//利用閉包實現了bind和 one; 減少程式碼量;
jQuery.each(["bind", "one"], function( i, name ) {
jQuery.fn[ name ] = function( type, data, fn ) {
// Handle object literals
//處理傳進來的是物件的情況, 呼叫對應的方法; 一介面的多種實用方法;
if ( typeof type === "object" ) {
for ( var key in type ) {
this[ name ](key, data, type[key], fn);
};
return this;
}; //修正引數 data , fn;
//data為什麼要===false $("div").bind("click",false,function() {});怎麼辦哇;
if ( jQuery.isFunction( data ) || data === false ) {
fn = data;
data = undefined;
}; //初始化繫結的函式
//如果是one的話就是重新定義事件函式, 這個事件函式也是一個閉包, 引用了fn, 需要強調的是 $.proxy的作用是設定匿名事件函式的guid和fn一樣;;
var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
jQuery( this ).unbind( event, handler );
return fn.apply( this, arguments );
}) : fn; //對unload的事件進行優化, 本身unload的事件就是不能一直掛在元素上面的;
if ( type === "unload" && name !== "one" ) {
this.one( type, data, fn );
} else {
for ( var i = 0, l = this.length; i < l; i++ ) {
//呼叫工具, 現在的引數一定是對的, 裡面就不用擔心使用者亂傳引數進來了;
jQuery.event.add( this[i], type, handler, data );
}
} return this;
};
}); //
jQuery.fn.extend({
unbind: function( type, fn ) {
// Handle object literals
// 這個和unbind做一樣的處理;
if ( typeof type === "object" && !type.preventDefault ) {
for ( var key in type ) {
this.unbind(key, type[key]);
}; } else {
for ( var i = 0, l = this.length; i < l; i++ ) {
//unbind;
jQuery.event.remove( this[i], type, fn );
}
} return this;
}, //delegate也是呼叫live哇;
delegate: function( selector, types, data, fn ) {
return this.live( types, data, fn, selector );
}, //unbind是呼叫die,1.6以後的版本好像沒有live和die了;
undelegate: function( selector, types, fn ) {
if ( arguments.length === 0 ) {
return this.unbind( "live" );
} else {
return this.die( types, null, fn, selector );
}
}, trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
}, //trigger 和triggerHandler的區別是 後者 觸發了當前的第一個元素的對應事件, 而且阻止了預設操作和冒泡;
triggerHandler: function( type, data ) {
if ( this[0] ) {
var event = jQuery.Event( type );
event.preventDefault();
event.stopPropagation();
jQuery.event.trigger( event, data, this[0] );
//事件物件有一個result, 說明 迭代執行事件的時候的返回值被儲存到了event.result去;
return event.result;
};
}, //toggle和hover就是對click進行了封裝而已;
toggle: function( fn ) {
// Save reference to arguments for access in closure
var args = arguments,
i = 1; // link all the functions, so any of them can unbind this click handler
// 把這幾個事件函式的guid設定成一樣的數字,保證了使用unbind的時候可以取消這個click事件;
// i從第一個開始迭代到最後一個;
while ( i < args.length ) {
jQuery.proxy( fn, args[ i++ ] );
};
/*
這個迴圈和for( var i=0; i<len ;i++); for( var i=0; i<len ;) {i++}這是一樣的;
while ( i < args.length ) {
jQuery.proxy( fn, args[ i ] );
i++;
};
*/
//又用了一個閉包, 繫結這個了fn這個時間;
return this.click( jQuery.proxy( fn, function( event ) {
// Figure out which function to execute
// i現在是總數;
/*
0%4 ==>> 0
1%4 ==>> 1
2%4 ==>> 2
3%4 ==>> 3
4%4 ==>> 0 //內部用的_data
jQuery._data = function ( elem, name, data ) {
return jQuery.data( elem, name, data, true );
};
*/
var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); // Make sure that clicks stop
//為什麼要阻止預設事件哇;
event.preventDefault(); //執行
// and execute the function
return args[ lastToggle ].apply( this, arguments ) || false;
}));
}, hover: function( fnOver, fnOut ) {
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
}
}); var liveMap = {
focus: "focusin",
blur: "focusout",
mouseenter: "mouseover",
mouseleave: "mouseout"
}; //兩個閉包, 減少程式碼量; //這裡面的i沒有什麼用, name才有用;
jQuery.each(["live", "die"], function( i, name ) {
jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
var type, i = 0, match, namespaces, preType,
//事件代理的text; 沒有的話傳進來預設為當前的元素的選擇符;
selector = origSelector || this.selector,
//如果有origSelector就是 當前的元素, 否則 document?, ,為context繫結事件;
context = origSelector ? this : jQuery( this.context ); //和bind unbind一樣,提供object的方式傳參; write less do more;
if ( typeof types === "object" && !types.preventDefault ) {
for ( var key in types ) {
context[ name ]( key, data, types[key], selector );
};
return this;
}; //處理引數, 實在不懂data有毛用哇;
//$("body").live("click",function(){},"div");
//$("body").live("click","",function(){},"div");
if ( jQuery.isFunction( data ) ) {
fn = data;
data = undefined;
}; //支援多事件的情況; $("body").live("click mousein mouseout","",function(){},"div");
types = (types || "").split(" "); while ( (type = types[ i++ ]) != null ) {
// rnamespace = /\.(.*)$/;
// 事件的名稱空間, 如果你綁定了click事件,而且要區分click事件的類別分別不同情況觸發,就可以使用名稱空間;
match = rnamespaces.exec( type );
namespaces = ""; if ( match ) {
/*
/dfd/.exec("eadfdsdfe.sdfsdfe");
["dfd"]
*/
namespaces = match[0]; //名稱空間
type = type.replace( rnamespaces, "" ); //型別
}; //系統沒有hover事件,把hover事件替換成mouseenter和mouseleave;
if ( type === "hover" ) {
types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
continue;
}; preType = type; //為了讓事件冒泡吧,所以做了處理;
/*
var liveMap = {
focus: "focusin",
blur: "focusout",
mouseenter: "mouseover",
mouseleave: "mouseout"
};
*/
if ( type === "focus" || type === "blur" ) {
types.push( liveMap[ type ] + namespaces );
type = type + namespaces;
} else {
//這個不靠譜吧,mouseenter和mouseleave 就chrome的低版本不支援啊, 為什麼要全部使用mouseover和mouseout進行模擬呢;
type = (liveMap[ type ] || type) + namespaces;
}; //現在還在閉包內部,所以要根據情況判斷是新增事件還是移除事件;
if ( name === "live" ) {
// bind live handler
//context = origSelector ? this : jQuery( this.context );別忘記了context是this的引用或者其他物件的引用;
for ( var j = 0, l = context.length; j < l; j++ ) {
//要繫結的物件
//liveConvert("click",".class0 .wa") ==>> "click.`class0&`wa"
//liveConvert("click",".class0 .wa #div") ==>> "click.`class0&`wa&#div"
//內部自己約定了事件代理的描述;
jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
//這個是事件的描述, 高版本的jQ把事件代理的描述和事件描述合併在一起了;
//preType指的是未對使用者傳進來事件名字進行處理的事件名字;
{ data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
};
} else {
// unbind live handler
//這個直接使用unbind 這樣好嗎 john....;
context.unbind( "live." + liveConvert( type, selector ), fn );
};
};
return this;
};
//live的總結 : 事件的代理是在live處理的,使用了live繫結的元素繫結的事件預設是live開頭, 後面就是懂得自然懂了。 比如:live."click.`class0&`wa&#div"
}); //liveHandler也是挺簡單, 主流程是根據事件物件的 事件描述物件 匹配出符合名稱空間的繫結函式, 然後讓他執行;
function liveHandler( event ) {
var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
elems = [],
selectors = [],
events = jQuery._data( this, eventKey ); if ( typeof events === "function" ) {
events = events.events;
} // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
return;
} //匹配合適的名稱空間哇;
if ( event.namespace ) {
namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
} event.liveFired = this; //複製一個事件描述物件的陣列;
var live = events.live.slice(0); for ( j = 0; j < live.length; j++ ) {
handleObj = live[j]; //名稱空間符合event.type 就把這個函式儲存起來;
if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
selectors.push( handleObj.selector ); } else {
live.splice( j--, 1 );
}
} //這個返回的是所有匹配的元素;
match = jQuery( event.target ).closest( selectors, event.currentTarget ); //這個是雙重迴圈,過濾合適的element
for ( i = 0, l = match.length; i < l; i++ ) {
close = match[i]; for ( j = 0; j < live.length; j++ ) {
handleObj = live[j]; if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
elem = close.elem;
related = null; // Those two events require additional checking
if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
event.type = handleObj.preType;
related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
} //把元素和事件物件儲存起來;
if ( !related || related !== elem ) {
elems.push({ elem: elem, handleObj: handleObj, level: close.level });
}
}
}
} //一個一個執行, event下的result屬性依然儲存著上一個事件的返回值;
for ( i = 0, l = elems.length; i < l; i++ ) {
match = elems[i]; if ( maxLevel && match.level > maxLevel ) {
break;
} event.currentTarget = match.elem;
event.data = match.handleObj.data;
event.handleObj = match.handleObj; ret = match.handleObj.origHandler.apply( match.elem, arguments ); //這個和handle裡面的程式碼重複了, jQ高版本做了優化;
if ( ret === false || event.isPropagationStopped() ) {
maxLevel = match.level; if ( ret === false ) {
stop = false;
}
if ( event.isImmediatePropagationStopped() ) {
break;
}
}
} return stop;
}; //提供給事件代理用的;
function liveConvert( type, selector ) {
return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
}; //shortcut, 提供例項方法上面快捷方式呼叫
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").split(" "), function( i, name ) { // Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
if ( fn == null ) {
fn = data;
data = null;
} return arguments.length > 0 ?
this.bind( name, data, fn ) :
this.trigger( name );
}; if ( jQuery.attrFn ) {
jQuery.attrFn[ name ] = true;
}
});

  快3點了,我*,睡了。。