因此必須在支持語法折疊的編輯器裡打開源碼。 根據折疊層次,我們可以很快知道: 所有 jQuery 的代碼都在一個函數中:
(function( window, undefined ) {
// jQuery 代碼
})(window);
這樣可以避免內部對象污染全局。傳入的參數1是 window, 參數2是 undefined , 加快js搜索此二對象的速度。
可以發現 jQuery 代碼是按這樣順序來組織:
下面的模塊可以用上面的模塊,上面的模塊不需要下面的模塊
總的代碼是這樣的框架:
var jQuery = (function() {
// 創建 jQuery 對象
var jQuery = function( selector, context ) {
// 略
};
// 創建 jQuery.fn 對象
jQuery.fn = jQuery.prototype = {
// 略
};
// 聲明 jQuery.extend
jQuery.extend = jQuery.fn.extend = function() {
// 略
};
// 使用 jQuery.extend 擴展自己
jQuery.extend({
// 略
});
// 浏覽器方面的一些瑣碎
// 略
// 定義全局對象
return (window.jQuery = window.$ = jQuery);
})();
從這裡知道: 平時所用的 $ 其實就是 jQuery 函數的別名。
jQuery對象似乎一直都是這東西:
var jQuery = function( selector, context ) {
// 實際上 jQuery 對象是 jQuery.fn.init 返回的。
return new jQuery.fn.init( selector, context, rootjQuery );
}
這個函數表示: 要想知道函數 jQuery 是什麼東西,必須看 jQuery.fn.init 對象。
同時這也解釋了為什麼寫代碼不需要 new jQuery。
再看第29行 - 97行, 都是一些變量聲明,這些變量在下面的函數用到。提取變量的好處: 對正則節約編譯的時間, 同時能在壓縮的時候獲得更小的結果。
這個fn 其實是 jQuery.prototype ,這也是為啥jQuery.fn 就是擴展 jQuery對象的唯一原因。
肯能有人會疑問, jQuery 返回 new jQuery.fn.init, 也就是說,平時的函數應該是 jQuery.fn.init.prototype 所有的成員,不是 jQuery.prototype 成員。當然原因也很簡單: jQuery.fn.init.prototype === jQuery.prototype (代碼 322 行)
jQuery 對js對象處理和中國人講話一樣繞。這裡總結下到底 jQuery 對象是個什麼家伙。
jQuery 是普通函數, 返回 jQuery.fn.init 對象的實例( new jQuery.fn.init() )。
然後 jQuery.fn === jQuery.prototype === jQuery.fn.init.prototype ,最後, jQuery返回的對象的成員和 jQuery.fn 的成員匹配。
jQuery.fn 下有很多成員,下面稍作介紹:
init - 初始化(下詳細說明)
constructor - 手動指定一個構造函數。 因為默認是 jQuery.fn.init
length - 讓這個對象更接近一個 原生的數組
size - 返回 length
toArray - 通過 Array.prototype slice 實現生成數組
get - 即 this[ num ] ,當然作了下 參數索引 的處理。
pushStack - 加入一個元素
ready - 浏覽器加載後執行(下詳細說明)
end - 通過保存的 prevObject 重新返回
each - 參考 http://www.cnblogs.com/Fooo/archive/2011/01/11/1932900.html
參考 http://www.cnblogs.com/rubylouvre/archive/2009/11/21/1607632.html
jQuery.fn.init 就是所謂的 $ 函數。 也就是說,平常的 $("#id") 就是 new jQuery.fn.init("#id");
這個函數很長,但代碼覆蓋率小。
init: function( selector, context, rootjQuery ) {
// 參數: selector 選擇器
// context 上下文
// rootjQuery 父節點
// 處理 $("")、 $(null) 和 $(undefined)
if ( !selector ) {
return this;
}
// 處理 $(DOMElement)
if ( selector.nodeType ) {
// 直接扔數組中, 就搞定了。
this.context = this[0] = selector;
this.length = 1;
return this;
}
// 處理 $("body") body 元素只存在一次,單獨找它
if ( selector === "body" && !context && document.body ) {
// 同樣扔數組中, 順便把 selector 更新更新。
this.context = document;
this[0] = document.body;
thisis.selector = "body";
this.length = 1;
return this;
}
// 處理 $(HTML 代碼 或者是 css 選擇器)
if ( typeof selector === "string" ) {
// 略
// 處理 $(函數)
} else if ( jQuery.isFunction( selector ) ) {
// 如果是函數,則執行 $(document).ready , 這樣 $(document).ready(func) 簡為 $(func)
return rootjQuery.ready( selector );
}
// 略。
// 如果傳入的是一個 Dom列表 ( getElementsByTagName 結果 ) 則轉為 jQuery 數組。
return jQuery.makeArray( selector, this );
}
// 這部分代碼是 上段中 略 的 也就是說是 jQuery(字符串) 處理。
// 檢查是否字符串是常用選擇器 (/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/)
match = quickExpr.exec( selector );
// 檢查是否正確匹配
if ( match && (match[1] || !context) ) {
// 處理: $(html) -> $(array)
if ( match[1] ) {
// 獲取正文,默認 document
context = context instanceof jQuery ? context[0] : context;
doc = (context ? context.ownerDocument || context : document);
// 如果傳入簡單的 "<標簽>", ( /^<(\w+)\s*\/?>(?:<\/\1>)?$/)
ret = rsingleTag.exec( selector );
// 返回 createElement("tag")
return jQuery.merge( this, selector );
// 處理: $("#id")
} else {
elem = document.getElementById( match[2] );
// 因為有的浏覽器 getElementById 不只返回 id匹配的,所以做檢查。
return this;
}
// 處理 $("標簽")
} else if ( !context && !rnonword.test( selector ) ) {
this.selector = selector;
this.context = document;
selector = document.getElementsByTagName( selector );
return jQuery.merge( this, selector );
//處理: $(選擇器, $(...))
} else if ( !context || context.jquery ) {
return (context || rootjQuery).find( selector );
// 處理: $(選擇器, 上下文)
// (相當於: $(上下文).find(選擇器)
} else {
return this.constructor( context ).find( selector );
}
這個函數用於 擴展函數
函數中含多個參數判斷,為了使用可以更靈活。
基本原理就是for(in),這裡不具體介紹了。
noConflict: function( deep ) {
window.$ = _$;
if ( deep ) {
window.jQuery = _jQuery;
}
return jQuery;
},
不多解釋了,就是讓 jQuery 恢復為全局的對象。
其中有2個函數:
jQuery.ready 觸發執行 readyList 中的所有函數
jQuery.bindReady 初始化讓 jQuery.ready 成功執行
bindReady: function() {
// 如果已經執行 bindReady 則返回。
if ( readyBound ) {
return;
}
readyBound = true;
// 如果頁面已經加載, 馬上執行 jQuery.ready
if ( document.readyState === "complete" ) {
return setTimeout( jQuery.ready, 1 );
}
// 標准浏覽器支持 DOMContentLoaded
if ( document.addEventListener ) {
// 你懂的
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// 為什麼還要 load ? , 因為有些時候 DOMContentLoaded 失敗(如 iframe) ,而 load 總是會成功, 所以,同時處理 DOMContentLoaded load, 在 jQuery.ready 中會刪除監聽函數,保證最後這個函數只執行一次
window.addEventListener( "load", jQuery.ready, false );
// IE浏覽器( IE 8 以下)
} else if ( document.attachEvent ) {
// 使用 onreadystatechange
document.attachEvent("onreadystatechange", DOMContentLoaded);
// 同理
window.attachEvent( "onload", jQuery.ready );
// 如果 IE 下且非 iframe, 這裡有個技巧。 見 doScrollCheck();
// 原理: 浏覽器在沒加載時 設置 scrollLeft 會錯誤,所喲每隔1秒廁所 是否 scrollLeft 成功,如果發現成功,則執行 jQuery.ready 。但這只對非 frame 會有用。
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},
bindReady 函數在執行 ready 時執行。(jQuery 1.4 之前版本都是 絕對執行,不管需不需要 ready 函數)
人人都說 jQuery.support 是個好東西。確實,這東西可以解決很多兼容問題。
jQuery.support 是基於檢測的浏覽器兼容方式。也就是說,創建一個元素, 看這個元素是否符合一些要求。
比如測試元素是否支持checkOn屬性,只要先 set check = 'on' 然後看浏覽器是否 get check == 'on'。
此部分源碼不具體介紹了。
jQuery的 data() 用於存儲一個字典。而這些數據最後都保存在 jQuery.cache ( 代碼 1283 行) , 全局對象存在 windowData ( 代碼 1279 行) 。
但如果正確根據對象找到其在 jQuery.cache 的存儲對象? 這就是 expando 字符串。
比如一個對象: elem 。
滿足: elem.expando = "jQuery12321";
那麼 jQuery.cache["jQuery12321"] 就是存儲這個 elem 數據的對象。
實際上, 不是 elem.expando 表示鍵值,而是 elem[ jQuery.expando ] 表示。
而一個對象數據又是一個字典,所以最後執行 jQuery.data(elem, 'events') 後就是:
jQuery.cache[elem[jQuery.expando]]['events'] 的內容 (jQuery.cache[elem[jQuery.expando]] = {} )
隊列是 jQuery 1.5 新增的。
主要用於特效等需要等待執行的時候。
隊列主要操作就是 進隊queue 出隊dequeue
jQuery 隊列內的數據:
如果沒有執行:
[將執行的1, 將執行的2]
現在開始執行 <將執行的1>, 如果 type 為空或 "fx", 隊列內數據:
["inprogress", 將執行的2]
執行完之後:
["將執行的2]
以上的這些數據都存在 jQuery.data(obj, (type || "fx") + "queue") (代碼 1520 - 1522行)
jQuery.delay 則用於延時執行一個函數。相當於把原來隊列更新為 setTImeout 後的結果。 (代碼 1589 -1598 行)
從這裡開始,需要了解一個函數jQuery.access。對於 attr ,css 之類的函數,如果需要返回值,只返回第一個元素的值,如果是設置值,則設置每個元素的值。這個神奇的效果就是 jQuery.access 搞定的。
jQuery.access代碼在 794 - 819 行
jQuery大部分函數都是依賴 jQuery.access 實現的,比如有一個函數 XX,對用戶而言,調用的是 jQuery.fn.XX, 而這個函數需要對多個元素(jQuery數組內的所有的節點) 操作,或者對1個元素操作, 通過 jQuery.access 轉換(不一定都是),最後只寫對1個元素的操作。這1個元素的操作往往是 jQuery.XX 函數,因此,我們往往能看到即存在 jQuery.XX, 又存在 jQuery.fn.XX, 而其實 jQuery.fn.XX 都是依靠 jQuery.XX 的,或者說jQuery.XX是底層函數, jQuery.fn.XX 是方便用戶的工具 。
jQuery.fn.attr 這個函數(代碼 1632 - 1634 行) 只有1句話,真正的實現是 jQuery.attr (代碼 1880 - 1988)
又是一個大於100行的函數
attr: function( elem, name, value, pass ) {
// 檢查是否為 nodeType 為 Element
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) {
return undefined;
}
// 如果這個屬性需要特殊對待。 height/width/left 等屬性需特殊計算
if ( pass && name in jQuery.attrFn ) {
return jQuery(elem)[name](value);
}
// 檢查是否為 XML 還 HTML
var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
// Whether we are setting (or getting)
set = value !== undefined;
// 修正名字, 比如 float 改 cssFloat
name = notxml && jQuery.props[ name ] || name;
// 只有在節點的時候執行。
if ( elem.nodeType === 1 ) {
// 在 IE7- 下, href src 屬性直接獲取會返回絕對位置,而不是真實的位置字符串,
// 要獲得它們的真實值,需要 elem.getAttribute("href", 2);
var special = rspecialurl.test( name );
// Safari 誤報默認選項。通過獲取父元素的已選擇索引來修復。
if ( name === "selected" && !jQuery.support.optSelected ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// 對 optgroups ,同理
if ( parent.parentNode ) {
parent.parentNode.selectedIndex;
}
}
}
// 檢查屬性是否存在, 有些時候 name in elem 會失敗,所以多次測試。
if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
// 如果設置屬性
if ( set ) {
// 在IE, 不能設置屬性 type 。
if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
jQuery.error( "type property can't be changed" );
}
// 如果 value === null, 表示移除屬性
if ( value === null ) {
if ( elem.nodeType === 1 ) {
elem.removeAttribute( name );
}
} else {
// 一切屬性設置就是1句話。。。
elem[ name ] = value;
}
}
// 表單索引元素獲取需要 getAttributeNode( name ).nodeValue
if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
return elem.getAttributeNode( name ).nodeValue;
}
// elem.tabIndex 特殊處理
if ( name === "tabIndex" ) {
var attributeNode = elem.getAttributeNode( "tabIndex" );
return attributeNode && attributeNode.specified ?
attributeNode.value :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
undefined;
}
return elem[ name ];
}
// 處理 style 屬性
if ( !jQuery.support.style && notxml && name === "style" ) {
if ( set ) {
elem.style.cssText = "" + value;
}
return elem.style.cssText;
}
if ( set ) {
// 這裡除了 IE, 其它屬性使用標准 setAttribute
elem.setAttribute( name, "" + value );
}
// 如果屬性不存在,返回 undefined, 而不是 null 或 "" 之類的。
if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
return undefined;
}
// 見上
var attr = !jQuery.support.hrefNormalized && notxml && special ?
// Some attributes require a special call on IE
elem.getAttribute( name, 2 ) :
elem.getAttribute( name );
// 同上
return attr === null ? undefined : attr;
}
// 如果不是 DOM 元素,檢查處理。
if ( set ) {
elem[ name ] = value;
}
return elem[ name ];
}
平時我們都是調用 click(func) 之類的函數, 而其實這些都是工具函數,真正和事件掛鉤的函數是
jQuery.fn.bind - 調用 jQuery.event.add
jQuery.fn.unbind - 調用 jQuery.event.remove
jQuery.fn.trigger - 調用 jQuery.event.trigger
jQuery.fn.one - 調用 jQuery.fn.bind,Query.fn.unbind
要想知道jQuery的事件原理,必須讀 jQuery.event.add (代碼 2012 - 2155 行)
add: function( elem, types, handler, data ) {
// 只對節點操作。
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}
// IE 無法傳遞 window,而是復制這個對象 。
if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
elem = window;
}
// 如果 handler === false, 也就是說就是阻止某事件,
// 這樣只要 bind("evt", false); 就是阻止此事件。
if ( handler === false ) {
handler = returnFalse;
} else if ( !handler ) {
return;
}
// handleObjIn 是內部處理句柄, handleObj 是直接使用的處理句柄。
var handleObjIn, handleObj;
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
}
// 為函數生成唯一的 guid 。具體下面介紹。
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
// 獲取一個節點的數據。
var elemData = jQuery.data( elem );
// 如果沒有數據,則直接返回。
if ( !elemData ) {
return;
}
// 避免和原生的js對象混淆。
var eventKey = elem.nodeType ? "events" : "__events__",
// 這裡就是關鍵。
// elemData 是存儲數據的位置, 而 elemData[ eventKey ] 就是存儲當前事件的對象。 elemData.handle 就是當前綁定的所有函數數組。
// 也就是說,當我們綁定一個函數時,會往 elemData.handle 放這個函數,然後事件觸發時,會遍歷 elemData.handle 中函數然後去執行。
// 肯能有人會問,為什麼這麼做,因為原生的DOM內部也有一個 函數數組,事件觸發後會執行全部函數。答案還是 兼容。
// 標准浏覽器使用 addEventListener
// IE 使用 attachEvent
// 而這2者還是有差距的。因為 addEventListener 執行函數的順序即添加函數的順序,然而 attachEvent 執行函數的順序和添加的順序是相反的。
// jQuery 使用自定義的 handler 數組,好處有:
// 因為最後僅綁定一次原生事件,事件觸發後,手動執行 數組中的函數。這樣保證兼容。
// 同時也可以知道到底綁定了什麼函數,可以方便 trigger 函數的完成。
events = elemData[ eventKey ],
eventHandle = elemData.handle;
// 一些功能。。
if ( typeof events === "function" ) {
eventHandle = events.handle;
events = events.events;
} else if ( !events ) {
if ( !elem.nodeType ) {
elemData[ eventKey ] = elemData = function(){};
}
elemData.events = events = {};
}
// 如果是第一次執行,需創建 eventHandle
if ( !eventHandle ) {
// eventHandle 就是真正綁定到原生事件的那個函數,這個函數用來執行events.hadlers 用。
elemData.handle = eventHandle = function() {
// Handle the second event of a trigger and when
// an event is called after a page has unloaded
return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
jQuery.event.handle.apply( eventHandle.elem, arguments ) :
undefined;
};
}
// 綁定函數和原生,這樣可以保證函數可執行為目前作用域。
eventHandle.elem = elem;
// 處理 jQuery(...).bind("mouseover mouseout", fn);
types = types.split(" ");
var type, i = 0, namespaces;
while ( (type = types[ i++ ]) ) {
handleObj = handleObjIn ?
jQuery.extend({}, handleObjIn) :
{ handler: handler, data: data };
// 略
// 綁定 type guid
handleObj.type = type;
if ( !handleObj.guid ) {
handleObj.guid = handler.guid;
}
// 獲取當前的函數數組。
var handlers = events[ type ],
special = jQuery.event.special[ type ] || {};
// 如果第一次,則創建這個數組。
if ( !handlers ) {
handlers = events[ type ] = [];
// 特殊事件要執行 setup 而不是標准 addEventListener。
// 此行用來支持自定義的事件。
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// 標准事件。 這裡綁定的為 eventHandle
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
// 自定義事件,執行 add
if ( special.add ) {
special.add.call( elem, handleObj );
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// 不管是不是首次,都放入目前綁定的函數。
handlers.push( handleObj );
// 為實現 trigger 。
jQuery.event.global[ type ] = true;
}
// 讓IE下可以正常回收 elem 內存。
elem = null;
},
框架的義務當然也包括 事件參數的修復。jQuery 是自定義事件對象, 這個對象模擬真實事件對象。
根據上文可以知道,真正綁定事件的是一個函數,這個函數執行時會先 生成自定義事件對象, 然後把此對象作為參數調用所有的 handler 。
jQuery 自定義事件是 jQuery.Event 。 (代碼 2583-2610 行)
jQuery.Event = function( src ) {這個函數做的就是模擬真實的事件對象。
// 支持 沒有 new jQuery.Event
if ( !this.preventDefault ) {
return new jQuery.Event( src );
}
// 略
}
此外,還要配合 jQuery.event.fix 使用。 (2470 - 2527行)
特殊事件都定義在 jQuery.event.special 。 ( 代碼 2537 - 2567 行 )
默認有 ready live beforeunload
一個特殊事件是jQuery內部特別處理的事件,它們可自定義這個事件如何綁定、添加、刪除或觸發。
代碼 2292 - 2403 行,目的就是為了模擬觸發某事件。這個函數支持模擬冒泡。
參考 http://www.cnblogs.com/rooney/archive/2008/12/03/1346449.html
由於專門解釋選擇器的文章比較多,這裡不多說了。
參考 http://www.cnblogs.com/rooney/archive/2008/12/02/1346135.html
http://www.cnblogs.com/rubylouvre/archive/2009/11/23/1607917.html
和節點有關的函數如: parent next 等這些查找n個節點的工具。
這些函數都依靠 jQuery.fn.nth() 通過 關系找到下個節點,如果這個節點是 Element, 則返回,否則繼續找
由於這些函數都是比較易懂的,這裡就不解釋了。
一些HACK技巧強調下:
jQuery 1.5 的clone 和 Mootools 的一樣,采用 cloneFixAttributes
(代碼 5196 - 5225 行)
cloneCopyEvent - 不是復制 data, 而是依據 data 重執行 bind
cloneFixAttributes - 先 clearAttributes ,然後 mergeAttributes ( IE 專用) 這樣可以保證屬性正確拷貝。
這個函數用來按 HTML 返回節點, 就是 $("復雜 html ") 所使用的。
這裡用到不常見的 document.createDocumentFragment();
如果是 <script> 則執行 jQuery.globalEval ( 全局執行)
為什麼不直接把 <script> 放入head ? 因為 單個 <script> 肯能導致洩漏。
刪除元素內容子節點。
參考 http://www.cnblogs.com/70buluo/archive/2009/06/03/1495040.html
參考 http://www.cnblogs.com/rooney/archive/2008/12/02/1346135.html
獲取元素的css屬性確實是很郁悶的事。jQuery 首先把一些屬性單獨處理, 其余的使用 style[ name ] || ( IE ? getComputedStyle( elem, null).getProperty( name ) : elem.currentStyle [name] 的方式獲取,並稍微做點兼容處理。
具體可以參考 http://www.cnblogs.com/rubylouvre/archive/2009/09/05/1559883.html
http://www.cnblogs.com/rubylouvre/archive/2009/11/21/1607255.html
jQuery 1.5 重寫了 ajax 模塊。
jQuery的 ajax其實也是使用一個模擬機制,而不是基於元素的 onreadystatechange
也就是說, jQuery自己有個函數, 每隔13ms 判斷 readystate是否 改了。
參考 http://www.cnblogs.com/qleelulu/archive/2008/04/21/1163021.html
參考 http://www.cnblogs.com/rooney/archive/2008/12/03/1346475.html
附加說明下特效轉換,
說到特效,很多人都知道這是依靠 setTimeout 完成的。但如何實現?
假設運動是 勻速的。從 x = x1 開始運動, 在 dt 中勻速運動到 x =x2 的位置。
而因為 需要在 dt 內把 x = x1 -> x2, 為了方便計算,特引入一個 delta, 滿足 當前的 x = x1 + (x2 - x1) * delta 。
所以整個動畫開始的時候 delta=0, 結束的時候則 dalta=1。這樣,無論多大的 x1, x2 ,特效的計算都變為 delta 的計算。
delta 隨時間變化,得到:
delta = f(t) (0 < t < dt) f 是一個映射。
每畫一幀的時候 , t 加1, 根據 f 重新算 delta 和 x 。
假如我們需要一秒顯示 fps 幀, 那就是說每1000/fps 毫秒 顯示一次, 也就說 每1000/fps毫秒就顯示一幀,即 setTimeout (畫幀, 1000/fps)。
總結下: 輸入是 fps, x1, x2, dt 則代碼:
var t = 0, f = 某個變換;
setInterval(function(){
var delta = f(t);
var x = x1 + (x2 - x1) * dalta;
// 更新顯示 x
t++;
}, 1000 / fps);
然後介紹 f 是什麼, f 輸入是時間,輸出 delta。f(0) = 0, f(dt) = 1。
在任一時刻: delta = (x - x1) / (x2 - x1)
當 x = 0 -> x
t = 0 -> dt
根據微分理解: f 是 x 方程的關於 t 的導函數。
當 x = k * t 為勻速運動,這裡的k = (x2 - x1) / dt
f = f|t = x' = k;
當 x = k * t 2 是拋物線,
f = x' = 2 * k * t;
一般情況, 使用 sin 函數變換柔和點,所以jquery默認使用sin變換,所以變換函數是
x = sin( t - 3π/2) ( 最低點 )
f = x' = -(cos( πt ) - 1) / 2
(代碼 7477 行)
jQuery.offset 用於管理定位,其它都是工具代碼。
jQuery.offset.initialize 測試浏覽器的定位法則。
jQuery.offse.bodyOffset 獲取 body 的定位。 因為這個定位和其它的不同,所有單獨測試。
jQuery.fn.offset 這是核心定位函數, 返回絕對位置,新浏覽器使用 getBoundingClientRect ,否則自己計算。
自己計算的方法就是遍歷父元素,把相對位置相加,返回的就是和父元素的相對位置,那和body相對位置就是絕對位置。
參考 http://www.cnblogs.com/xuld/archive/2011/02/13/1953907.html
jQuery.fn.position jQuery.fn.position 返回和 style.left 一樣。計算方法就是先 算 offset 和父節點(offsetParent) offset, 那麼就可以得到偏移。
分析源碼的時候可以對照代碼理解。jQuery雖然復雜但代碼還是好讀的。解讀源碼只要是為了更好地使用,所以知道源碼後,就自然知道為什麼jQuery可以做這麼多事。
但jQuery始終不是唯一的框架,感歎它的設計同時,還應該看一下其它性格的框架,比如 Mootools 。