jQuery為我們提供了如下方法來支持 Ajax和JSONP跨域訪問 全部是加到jQuery的靜態方法中去
1. jQuery.load( url, [data], [callback] ) :載入遠程 HTML 文件代碼並插入至 DOM 中。
2. jQuery.get( url, [data], [callback] ):使用GET方式來進行異步請求。
3. jQuery.post( url, [data], [callback], [type] ) :使用POST方式來進行異步請求。
4. jQuery.getScript( url, [callback] ) : 通過 GET 方式請求載入並執行一個 JavaScript 文件。
5. jQuery.ajax( options ) : 通過 HTTP 請求加載遠程數據。
我把請求Script的部分和JSONP跨域部分給刪掉了 現在比較簡潔
如下 繼續擴展jQuery
//jQuery ajax包裝 jQuery.extend({ //HTTP get方法 get: function (url, data, callback, type) { // shift arguments if data argument was ommited if (jQuery.isFunction(data)) { callback = data; data = null; } return jQuery.ajax({ type: "GET", url: url, data: data, success: callback, dataType: type }); }, //HTTP POST方法 post: function (url, data, callback, type) { if (jQuery.isFunction(data)) { callback = data; data = {}; } return jQuery.ajax({ type: "POST", url: url, data: data, success: callback, dataType: type }); }, //獲得JSON數據 getJSON: function (url, data, callback) { return jQuery.get(url, data, callback, "json"); }, //設置全局 AJAX 默認選項。 ajaxSetup: function (settings) { jQuery.extend(jQuery.ajaxSettings, settings); }, ajaxSettings: { url: location.href, type: "GET", timeout: 0, contentType: "application/x-www-form-urlencoded", processData: true, async: true, data: null, username: null, password: null, accepts: { xml: "application/xml, text/xml", html: "text/html", json: "application/json, text/javascript", text: "text/plain", _default: "*/*" } }, //為下一次請求緩存Last-Modified頭部. lastModified: {}, ajax: function (s) { //兩次繼承s s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); var status, data, type = s.type.toUpperCase(); //我把jsonp和script取消了,可惜了 可以直接在頁面上插入script來完成跨域 // If data is available, append data to url for get requests if (s.data && type == "GET") { s.url += (s.url.match(/\?/) ? "&" : "?") + s.data; // IE likes to send both get and post data, prevent this s.data = null; } var requestDone = false; //比較簡單的創建請求對象;微軟在IE7上並沒有正確地實現XMLHttpRequest var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); // Open the socket // 只是說open用於初始化一個准備發起仍在'pending'狀態中的請求 if (s.username) xhr.open(type, s.url, s.async, s.username, s.password); else xhr.open(type, s.url, s.async); // Need an extra try/catch for cross domain requests in Firefox 3 try { //// 如果需要一個過期頭, 那就設置這個過期頭.過期頭所標識的日期一般用於瀏覽器的緩存設置. if (s.ifModified) xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT"); // 設置頭部,以便能使服務器知道這是一個通過XMLHttpRequest發送的請求. xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); //設置接收數據的類型 xhr.setRequestHeader("Accept", s.dataType && s.accepts[s.dataType] ? s.accepts[s.dataType] + ", */*" : s.accepts._default); } catch (e) {} //等待返回的請求 var onreadystatechange = function (isTimeout) { // The transfer is complete and the data is available, or the request timed out if (!requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout")) { requestDone = true; // clear poll interval if (ival) { clearInterval(ival); ival = null; } status = isTimeout == "timeout" && "timeout" || !jQuery.httpSuccess(xhr) && "error" || s.ifModified && jQuery.httpNotModified(xhr, s.url) && "notmodified" || "success"; if (status == "success") { // Watch for, and catch, XML document parse errors try { // process the data (runs the xml through httpData regardless of callback) data = jQuery.httpData(xhr, s.dataType, s.dataFilter); } catch (e) { status = "parsererror"; } } //如果設置了ifModified為true,對響應頭進行緩存 //下次請求相同url的時候可以看看請求的頁面的修改日期是否晚過這個日期 if (status == "success") { // Cache Last-Modified header, if ifModified mode. var modRes; try { modRes = xhr.getResponseHeader("Last-Modified"); } catch (e) {} // swallow exception thrown by FF if header is not available if (s.ifModified && modRes) jQuery.lastModified[s.url] = modRes; success(); } else jQuery.handleError(s, xhr, status); // 觸發complete事件, 運行綁定在這個事件上事件監聽函數 complete(); // 把xhr設為null, 讓垃圾回收器對xhr進行回收,防止內存泄漏 if (s.async) xhr = null; } }; //如果是異步的請求, 設置請求重試, 一次不成功就再來直到成功或者超時 if (s.async) { // don't attach the handler to the request, just poll it instead var ival = setInterval(onreadystatechange, 13); // Timeout checker if (s.timeout > 0) setTimeout(function () { // 如果xhr不為null, 說明請求正在進行,取消這次請求, 因為超時了 if (xhr) { // Cancel the request xhr.abort(); //如果請求還沒完成,馬上調用函數這樣requestDone為true if (!requestDone) onreadystatechange("timeout"); } }, s.timeout); } //發送請求 try { xhr.send(s.data); } catch (e) { jQuery.handleError(s, xhr, null, e); } //在firefox 1.5中,同步請求並不能觸發statechange事件.所以手動觸發 if (!s.async) onreadystatechange(); function success() { // If a local callback was specified, fire it and pass it the data if (s.success) s.success(data, status); } function complete() { // Process result if (s.complete) s.complete(xhr, status); } // return XMLHttpRequest to allow aborting the request etc. return xhr; }, //jQuery.ajax方法中出現的錯誤處理函數 handleError: function (s, xhr, status, e) { // If a local callback was specified, fire it if (s.error) s.error(xhr, status, e); }, //判斷當前這個請求是否是成功 httpSuccess: function (xhr) { try { // IE有一個錯誤, 那就是有時候應該返回204(No Content)但是它卻返回1223 //safari在文檔沒有修改時(304)得到的status會等於undefined,所以把這種情況也當作是成功 return !xhr.status && location.protocol == "file:" || (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || xhr.status == 1223 || jQuery.browser.webkit && xhr.status == undefined; } catch (e) {} return false; }, //判斷請求回來的服務器響應是不是"NotModified". httpNotModified: function (xhr, url) { try { var xhrRes = xhr.getResponseHeader("Last-Modified"); //Firefox 總是返回200. 還是對比一下Last-Modified的日期穩妥一些. return xhr.status == 304 || xhrRes == jQuery.lastModified[url] || jQuery.browser.webkit && xhr.status == undefined; } catch (e) {} return false; }, /*獲取XMLHTTPRequest的響應數據.允許對數據使用自定義的函數進行預處理.並根據用戶提供的數據類型對響應數據做不同的處理. 最后將數據返回.*/ httpData: function (xhr, type, filter) { var ct = xhr.getResponseHeader("content-type"), xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0, data = xml ? xhr.responseXML : xhr.responseText; if (xml && data.documentElement.tagName == "parsererror") throw "parsererror"; // Get the JavaScript object, if JSON is used. if (type == "json") data = eval("(" + data + ")"); return data; } });
測試一下 ajax get post 方法
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ex7</title> <script type="text/javascript" src="jquery.js"></script> <!-- <script type="text/javascript" src="jquery-1.2.6.js"></script>--> </head> <body> <div id="content"> <button id="send">點擊Ajax</button> <button id="sendGet">點擊Get</button> <button id="sendPost">點擊Post</button> </div> <script type="text/javascript"> $().ready(function() { console.log("激動人心的jQuery ready函數"); }); $("#send").click(function() { $.ajax({ url: "s.txt", type: "post", dataType: "json", success: function(data, status) { console.log(data); } }); }); $("#sendGet").click(function() { $.get("s.txt", function(data, status) { console.log(data); }, "json"); }); $("#sendPost").click(function() { $.post("s.txt", function(data, status) { console.log(data); }, "json"); }); </script> </body> </html>
其中 s.txt的內容如下
{name:"bq",age:10}
測試結果
控制台輸出
附上到現在的全部代碼 大概1000行而已
//自調函數把window這個全局變量傳入 (function () { // 把jQuery和$另存一份 以便發生沖突的時候再使用 var _jQuery = window.jQuery, _$ = window.$; // 在上次的代碼上添加選擇器機制 var jQuery = window.jQuery = window.$ = function (selector, context) { return new jQuery.fn.init(selector, context); }; // 對 HTML strings or ID strings進行判定 /* * 可以把它看作是兩個正則表達式串: 第一個:^[^<]*(<(.|\s)+>)[^>]*$ 第二個:^#(\w+)$ /^ 字符串開始 [^<]* * 匹配不包含<號的任意長度字符,長度可以為0 (<(.|\s)+ 匹配任意字符或空白符,長度必須大於等於1 >)[^>]* * 匹配不包含>號的任意字符,長度可以為0 $ 字符串結束 | 或 ^ 字符串開始 # 匹配符號# (\w+) * 匹配a-zA-z0-9,也就是所有英文字母及數字,長度大於等於1 $/; 結束 */ var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$|^.(\w+)$|/; jQuery.fn = jQuery.prototype = { init: function (selector, context) { selector = selector || document; context = context || document; // 如果傳入的是DOM 節點對象 if (selector.nodeType) { this[0] = selector; this.length = 1; return this; } // 對於選擇器是字符串有可能是#id if (typeof selector == "string") { // Are we dealing with HTML string or an ID? var match = quickExpr.exec(selector); /*console.log("match result is "+match);*/ // Verify a match, and that no context was specified for #id if (match && (match[0] || !context)) { if (match[1]) { selector = jQuery.clean([match[1]], context); return this.setArray(jQuery.makeArray(selector)); } else if (match[3]) { var elem = document.getElementById(match[3]); this.length = 1; return jQuery(elem); selector = []; } else if (match[4]) { //我選擇使用CSS3選擇器 var elements = document.getElementsByClassName(match[4]); //轉化為數組加入到jQuery類數組對象 return this.setArray(jQuery.makeArray(elements)); } else { //CSS3選擇器 估計這里也不會執行 var elements = document.querySelectorAll(match[0]); return this.setArray(jQuery.makeArray(elements)); } } } //解決不了的全部交給CSS3選擇器 出故障暫時不管 var others = document.querySelectorAll(selector); // console.log("this is:"+Object.prototype.toString.call(this)); return this.setArray(jQuery.makeArray(others)); }, // 新增加一個方法用來取得或設置jQuery對象的html內容 html: function (val) { // 遍歷通過回調函數來設置innerHTML為val參數 return jQuery.each(this, function (val) { this.innerHTML = val; }, val); }, length: 0, jquery: "1.0.0", author: "BaiQiang", size: function () { return this.length; }, //直接調用jQuery靜態函數 each: function (callback, args) { return jQuery.each(this, callback, args); }, //擴展一些有用的方法 setArray: function (elems) { this.length = 0; Array.prototype.push.apply(this, elems); return this; }, attr: function (name, value, type) { //為所有匹配的元素設置一個計算的屬性值。 //不提供值,而是提供一個函數,由這個函數計算的值作為屬性值。 var options = name; // Look for the case where we're accessing a style value if (name.constructor == String) // 如果沒有傳入要設置的值,則要獲得該屬性的值. if (value === undefined) //運算符&&的行為是這樣的:對於它兩邊表達式,誰要把運算給中止了,就返回誰. return this[0] && jQuery[type || "attr"](this[0], name); else { //處理鍵值對 options = {}; options[name] = value; } //遍歷所有options 字符串就是一次 鍵值對循環 return this.each(function (i) { // Set all the styles for (name in options) jQuery.attr(type ? //如果有傳入type,就表示要設置樣式屬性;如果沒有則表示要設置一般的屬性 this.style : this, name, jQuery.prop(this, options[name], type, i, name)); }); }, css: function (key, value) { if ((key == 'width' || key == 'height') && parseFloat(value) < 0) value = undefined; //其實就是調用attr 並設置第三個參數type為"curCSS" return this.attr(key, value, "curCSS"); }, }; // 簡單的遍歷函數 /* * jQuery.each = function(obj, callback, args) { for (var i = 0; i < * obj.length; i++) { callback.call(obj[i], args); } return obj; }; */ // 需要深入理解一下jQuery的復制函數寫法 jQuery.extend = jQuery.fn.extend = function () { // 用一個或多個其他對象來擴展一個對象,返回被擴展的對象。 //jQuery.extend(settings, options); //var settings = jQuery.extend({}, defaults, options); //target是被擴展的對象,默認是第一個參數(下標為0)或者是一個空對象{} var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; /* * 如果傳進來的首個參數是一個boolean類型的變量,那就證明要進行深度拷貝。 * 而這時傳進來的argumens[1]就是要拷貝的對象.如果是這種情況,那就要做一些"矯正"工作, * 因為這個時候,target變量指向的是一個布爾變量而不是我們要拷貝的對象. */ if (target.constructor == Boolean) { deep = target; target = arguments[1] || {}; // 這樣的話使得i=2直接跳過前兩個參數 i = 2; } // 如果target不是objet 並且也不是function 就默認設置它為{}; if (typeof target != "object" && typeof target != "function") target = {}; // 一個參數的就是擴展jQuery對象本身 if (length == i) { target = this; --i; } for (; i < length; i++) // 非null的擴展對象才把它擴展到被擴展對象上來. if ((options = arguments[i]) != null) // Extend the base object for (var name in options) { // target是被擴展對象 options是擴展對象, 它的方法或屬性將會被擴展到target上 var src = target[name], copy = options[name]; // target和copy如果相等還深拷貝的話就出問題了 if (target === copy) continue; // Recurse if we're merging object values // 遞歸src和copy深度拷貝即對每一個屬性遞歸 // 要是沒有nodeType, 就是非Dom對象引用, 可以對它進行深度拷貝 if (deep && copy && typeof copy == "object" && !copy.nodeType) target[name] = jQuery.extend(deep, // Never move original objects, clone them src || (copy.length != null ? [] : {}), copy); // Don't bring in undefined values // 如果要加進來的引用不是對象的引用(只要不是undefined ) 那就把引用加進來 // 可能是覆蓋也可能是新建name這個屬性或方法 else if (copy !== undefined) target[name] = copy; } // 返回擴展后的被擴展對象 return target; }; //定義一個會在CSS設置中用到的正則表達式 var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; // 上次我寫了jQuery的extend函數 用來拷貝對象 這次我們就來使用一下來完成一些有意義的事情 jQuery.extend({ getName: function () { return "This is BaiQiang's jQuery"; }, noConflict: function (deep) { // 擴展jQuery對象本身。 // 使用這個函數必須以jQuery 開頭,不能用$開頭 // 把保存的_$還回給$ window.$ = _$; if (deep) window.jQuery = _jQuery; return jQuery; }, // 判斷傳入的參數是否一個函數 isFunction: function (fn) { return typeof fn == "function"; // 簡寫一下,不用jQuery的方法 // return !!fn && typeof fn != "string" && !fn.nodeName && // fn.constructor != Array && /^[\s[]?function/.test( fn + // "" ); }, // 判斷參數是否為一個XML節點 isXMLDoc: function (elem) { // body是HTMLDocument特有的節點常用這個節點來判斷當前的document是不是一個XML的文檔引用 return elem.documentElement && !elem.body || elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; }, trim: function (data) { // 第一部分^\s第二部分\s+$ 替換為空 return (data || "").replace(/^\s+|\s+$/g, ""); }, // 在全局的作用域中運行腳本 globalEval: function (data) { // 調用trim去除兩邊空格---見上 data = jQuery.trim(data); if (data) { // 動態創建節點 var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; if (jQuery.browser.msie) script.text = data; else script.appendChild(document.createTextNode(data)); // 兼容問題 Use insertBefore instead of appendChild to circumvent an IE6 bug. // 方法:可向節點的子節點列表的末尾添加新的子節點。語法:appendChild(newchild) // insertBefore() 方法:可在已有的子節點前插入一個新的子節點。語法 // :insertBefore(newchild,refchild) head.insertBefore(script, head.firstChild); head.removeChild(script); } }, // 繼續擴建 這次增加一個each方法 相比以前那個簡單的each方法高級了許多 each: function (object, callback, args) { // 以每一個匹配的元素作為上下文來執行一個函數。 // 意味着,每次執行傳遞進來的函數時, // 函數中的this關鍵字都指向一個不同的DOM元素(每次都是一個不同的匹配元素)。 // 而且,在每次執行函數時,都會給函數傳遞一個表示作為執行環境的元素在匹配的元素集合中所處位置的數字值作為參數(從零開始的整形)。 // 返回 'false' 將停止循環 (就像在普通的循環中使用 'break')。 // 返回 'true' 跳至下一個循環(就像在普通的循環中使用'continue')。 var name, i = 0, length = object.length; if (args) { // 對象如果沒有length就直接遍歷否則用i下標遍歷 if (length == undefined) { for (name in object) // 回調函數把當前對象指定為object[name] if (callback.apply(object[name], args) === false) break; } else for (; i < length;) if (callback.apply(object[i++], args) === false) break; // A special, fast, case for the most common use of each // 沒有參數的調用call和apply不同的就是參數傳入的是下標或屬性以及代表的值 } else { if (length == undefined) { for (name in object) if (callback.call(object[name], name, object[name]) === false) break; } else for (var value = object[0]; i < length && callback.call(value, i, value) !== false; value = object[++i]) {} } return object; }, //屬性操作 CSS操作也用到 attr: function (elem, name, value) { // 不設置文本或是注釋節點 if (!elem || elem.nodeType == 3 || elem.nodeType == 8) return undefined; var notxml = !jQuery.isXMLDoc(elem), //是setting呢還是getting set = value !== undefined; //一些屬性名字在Js中的表述並不是原來的屬性名字.如class,在JavaScript中就是className. name = notxml && jQuery.props[name] || name; if (elem.tagName) { //特殊元素 var special = /href|src|style/.test(name); /*//webkit bug? 我其實不知道 說不定已經修復了 先不管它了 if ( name == "selected" && jQuery.browser.webkit) elem.parentNode.selectedIndex;*/ //如果elem的屬性中有name所指示的屬性 && elem不是XML類型節點 && 不是要特殊對待的href/src/style if (name in elem && notxml && !special) { //set的時候有些值不可以改變 if (set) { // We can't allow the type property to be changed (In IE problem) if (name == "type" && jQuery.nodeName(elem, "input") && elem.parentNode) throw "屬性不可更改"; //設置屬性 elem[name] = value; } //如果是表單屬性 使用getAttributeNode if (jQuery.nodeName(elem, "form") && elem.getAttributeNode(name)) return elem.getAttributeNode(name).nodeValue; // 表示元素不是一個form元素, 直接就把元素的值返回 return elem[name]; } if (set) // convert the value to a string (all browsers do this but IE) see #1070 //如果屬性是一個非string的值, 除IE外所有的瀏覽器都能很好地工作.將value變成一個string elem.setAttribute(name, "" + value); var attr = notxml && special //getAttribute在只有一個參數. 而IE可以用兩個參數. // Some attributes require a special call on IE ? elem.getAttribute(name, 2) : elem.getAttribute(name); //返回undefined就說明屬性設置失敗 return attr === null ? undefined : attr; } //並非設值而是把匹配到的字符轉成大寫,實際上是想做這樣的效果:"margin-Top" -> "marginTop" name = name.replace(/-([a-z])/ig, function (all, letter) { return letter.toUpperCase(); }); //set設置值 if (set) elem[name] = value; return elem[name]; }, // 再寫一些對array操作的擴展 makeArray: function (array) { // 將類數組對象轉換為數組對象。 // 類數組對象有 length 屬性,其成員索引為 0 至 length - 1。實際中此函數在 jQuery // 中將自動使用而無需特意轉換。 var ret = []; if (array != null) { var i = array.length; // the window, strings and functions also have 'length' // 排除null window string function.. if (i == null || array.split || array.setInterval || array.call) ret[0] = array; else while (i) ret[--i] = array[i]; } return ret; }, inArray: function (elem, array) { // 確定第一個參數在數組中的位置(如果沒有找到則返回 -1 ) for (var i = 0, length = array.length; i < length; i++) // Use === because on IE, window == document if (array[i] === elem) return i; return -1; }, // 把兩個數組拼接起來(將第二個數組接到第一個數組的尾部) merge: function (first, second) { // We have to loop this way because IE & Opera overwrite the // length expando of getElementsByTagName var i = 0, elem, pos = first.length; // Also, we need to make sure that the correct elements are // being returned // 在一個使用'*'的選擇器中,IE會返回注釋節點. if (jQuery.browser.msie) { while (elem = second[i++]) if (elem.nodeType != 8) first[pos++] = elem; } else while (elem = second[i++]) first[pos++] = elem; return first; }, // 數組去重 面試還考過 unique: function (array) { var ret = [], done = {}; try { for (var i = 0, length = array.length; i < length; i++) { // if(!done[array[i]) var id = jQuery.data(array[i]); if (!done[id]) { done[id] = true; ret.push(array[i]); } } } catch (e) { // 遇到問題就返回原來的數組 ret = array; } return ret; }, // 過濾數組元素 參見ES5的 fliter grep: function (elems, callback, inv) { // (可選) 如果 "invert" 為 false 或為設置,則函數返回數組中由過濾函數返回 true 的元素, // / 當"invert" 為 true,則返回過濾函數中返回 false 的元素集。 var ret = []; // Go through the array, only saving the items // that pass the validator function for (var i = 0, length = elems.length; i < length; i++) if (!inv != !callback(elems[i], i)) ret.push(elems[i]); return ret; }, // 參見ES5中的map不解釋 map: function (elems, callback) { /* * 將一個數組中的元素轉換到另一個數組中。 作為參數的轉換函數會為每個數組元素調用, * 而且會給這個轉換函數傳遞一個表示被轉換的元素作為參數。 轉換函數可以返回轉換后的值、null(刪除數組中的項目) * 或一個包含值的數組,並擴展至原始數組中。 * 為每個數組元素調用,而且會給這個轉換函數傳遞一個表示被轉換的元素作為參數。函數可返回任何值。 * 另外,此函數可設置為一個字符串,當設置為字符串時,將視為“lambda-form”(縮寫形式?) 其中 a * 代表數組元素。如“a * a”代表“function(a){ return a * a; }” */ var ret = []; // Go through the array, translating each of the items to // their new value (or values). for (var i = 0, length = elems.length; i < length; i++) { var value = callback(elems[i], i); if (value != null) ret[ret.length] = value; } return ret.concat.apply([], ret); }, nodeName: function (elem, name) { //檢查指定的元素里是否有指定的DOM節點的名稱。 /// <param name="elem" type="Element">要檢查的元素</param> /// <param name="name" type="String">要確認的節點名稱</param> /// <returns type="Boolean">如果指定的節點名稱匹配對應的節點的DOM節點名稱返回true; 否則返回 false</returns> return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); }, //學習jQuery如何處理字符串為DOM元素 其實還沒有全明白 clean: function (elems, context) { var ret = []; context = context || document; // !context.createElement fails in IE with an error but returns typeof 'object' if (typeof context.createElement == 'undefined') context = context.ownerDocument || context[0] && context[0].ownerDocument || document; jQuery.each(elems, function (i, elem) { //直接返回 if (!elem) return; //數字直接轉字符串 if (elem.constructor == Number) elem += ''; // 字符串的話就轉為DOM元素 if (typeof elem == "string") { // Fix "XHTML"-style tags in all browsers elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function (all, front, tag) { return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? all : front + "></" + tag + ">"; }); // Trim whitespace, otherwise indexOf won't work as expected var tags = jQuery.trim(elem).toLowerCase(), div = context.createElement("div"); var wrap = // option or optgroup !tags.indexOf("<opt") && [1, "<select multiple='multiple'>", "</select>"] || !tags.indexOf("<leg") && [1, "<fieldset>", "</fieldset>"] || tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && [1, "<table>", "</table>"] || !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] || // <thead> matched above (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] || !tags.indexOf("<col") && [2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"] || // IE can't serialize <link> and <script> tags normally jQuery.browser.msie && [1, "div<div>", "</div>"] || [0, "", ""]; // Go to html and back, then peel off extra wrappers div.innerHTML = wrap[1] + elem + wrap[2]; // Move to the right depth while (wrap[0]--) div = div.lastChild; // Remove IE's autoinserted <tbody> from table fragments if (jQuery.browser.msie) { // String was a <table>, *may* have spurious <tbody> var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ? div.firstChild && div.firstChild.childNodes : // String was a bare <thead> or <tfoot> wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ? div.childNodes : []; for (var j = tbody.length - 1; j >= 0; --j) if (jQuery.nodeName(tbody[j], "tbody") && !tbody[j].childNodes.length) tbody[j].parentNode.removeChild(tbody[j]); // IE completely kills leading whitespace when innerHTML is used if (/^\s/.test(elem)) div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]), div.firstChild); } elem = jQuery.makeArray(div.childNodes); } if (elem.length === 0 && (!jQuery.nodeName(elem, "form") && !jQuery.nodeName(elem, "select"))) return; if (elem[0] == undefined || jQuery.nodeName(elem, "form") || elem.options) ret.push(elem); else ret = jQuery.merge(ret, elem); }); return ret; }, //prop擴展方法 prop: function (elem, value, type, i, name) { if (jQuery.isFunction(value)) value = value.call(elem, i); // 對於CSS屬性自動+'px' return value && value.constructor == Number && type == "curCSS" && !exclude.test(name) ? value + "px" : value; }, }); // 判斷瀏覽器 var userAgent = navigator.userAgent.toLowerCase(); jQuery.browser = { version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], webkit: /webkit/.test(userAgent), opera: /opera/.test(userAgent), msie: /msie/.test(userAgent) && !/opera/.test(userAgent), mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent) }; //關於float的IE問題處理 var styleFloat = jQuery.browser.msie ? "styleFloat" : "cssFloat"; //關於CSS方面小小擴展一下 有些css值用JS操作並不是原來的 典型的"class"->"className" jQuery.extend({ props: { "for": "htmlFor", "class": "className", "float": styleFloat, cssFloat: styleFloat, styleFloat: styleFloat, readonly: "readOnly", maxlength: "maxLength", cellspacing: "cellSpacing" } }); var expando; //接下來先學習jQuery的事件處理 來源Dean Edwards' addEvent library //我想把它簡化一下 可能效率會變差 jQuery.event = { // Bind an event to an element // Original by Dean Edwards add: function (elem, types, handler, data) { //不處理文本和注釋節點 if (elem.nodeType == 3 || elem.nodeType == 8) return; //IE中, 如果把window對象作為函數參數傳遞的話往往不能正確傳遞(即函數內部根本不知道它是window對象).於是利用下面這個if來判斷是不是在IE瀏覽器中以及檢查傳進來的elem會不會可能是window對象, 如果是, 就讓elem引用重新指向window //因為只有window 有setInterval方法 if (jQuery.browser.msie && elem.setInterval) elem = window; handler.data = data; jQuery.each(types.split(/\s+/), function (index, type) { // Namespaced event handlers var parts = type.split("."); type = parts[0]; handler.type = parts[1]; if (!jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false) { // IE和其他瀏覽器兼容性 if (elem.addEventListener) elem.addEventListener(type, handler, false); else if (elem.attachEvent) elem.attachEvent("on" + type, handler); } }); // Nullify elem to prevent memory leaks in IE elem = null; }, // 仍然把緩存機制取消了 簡單化一下 remove: function (elem, types, handler) { //不處理文本和注釋節點 if (elem.nodeType == 3 || elem.nodeType == 8) return; // Unbind all events for the element if (types == undefined || (typeof types == "string" && types.charAt(0) == ".")) for (var type in types) this.remove(elem, type + (types || "")); else { // Handle multiple events seperated by a space // jQuery(...).unbind("mouseover mouseout", fn); jQuery.each(types.split(/\s+/), function (index, type) { // Namespaced event handlers var parts = type.split("."); type = parts[0]; if (!jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false) { if (elem.removeEventListener) elem.removeEventListener(type, elem, false); else if (elem.detachEvent) elem.detachEvent("on" + type, elem); } }); } }, //修正event 比較麻煩 fix: function (event) { //如果事件已經包裝過 if (event[expando] == true) return event; //保存原來的event並且復制一個 var originalEvent = event; event = { originalEvent: originalEvent }; var 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(" "); for (var i = props.length; i; i--) event[props[i]] = originalEvent[props[i]]; //標記它已經被包裝過 event[expando] = true; //下面大部分是兼容性處理 // add preventDefault and stopPropagation since // they will not work on the clone event.preventDefault = function () { // if preventDefault exists run it on the original event if (originalEvent.preventDefault) originalEvent.preventDefault(); // otherwise set the returnValue property of the original event to false (IE) originalEvent.returnValue = false; }; event.stopPropagation = function () { // if stopPropagation exists run it on the original event if (originalEvent.stopPropagation) originalEvent.stopPropagation(); // otherwise set the cancelBubble property of the original event to true (IE) originalEvent.cancelBubble = true; }; // Fix timeStamp event.timeStamp = event.timeStamp || new Date(); // Fix target property, if necessary if (!event.target) event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either // check if target is a textnode (safari) if (event.target.nodeType == 3) event.target = event.target.parentNode; // Add relatedTarget, if necessary 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; }, //省略了其他special方法 special: { ready: { setup: function () { // Make sure the ready event is setup bindReady(); return; }, teardown: function () { return; } } } }; //添加到用戶使用的接口上 jQuery.fn.extend({ bind: function (type, data, fn) { /*為每一個匹配元素的特定事件(像click)綁定一個事件處理器函數。 這個事件處理函數會接收到一個事件對象,可以通過它來阻止(瀏覽器)默認的行為。 如果既想取消默認的行為,又想阻止事件起泡,這個事件處理函數必須返回false。多數情況下,可以把事件處理器函數定義為匿名函數。 在不可能定義匿名函數的情況下,可以傳遞一個可選的數據對象作為第二個參數(而事件處理器函數則作為第三個參數)。 內建事件類型值有: blur, focus, load, resize, scroll, unload, click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, error .*/ return type == "unload" ? this.one(type, data, fn) : this.each(function () { jQuery.event.add(this, type, fn || data, fn && data); }); }, //把one方法 省略掉 unbind: function (type, fn) { /// bind()的反向操作,從每一個匹配的元素中刪除綁定的事件。 return this.each(function () { jQuery.event.remove(this, type, fn); }); }, hover: function (fnOver, fnOut) { return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut); }, ready: function (fn) { // Attach the listeners bindReady(); // If the DOM is already ready if (jQuery.isReady) //已經ready就執行 fn.call(document, jQuery); else // 添加到等待列隊 jQuery.readyList.push(function () { return fn.call(this, jQuery); }); return this; } }); //Event的加載完成判斷方法、 jQuery.extend({ isReady: false, readyList: [], // Handle when the DOM is ready ready: function () { // Make sure that the DOM is not already loaded if (!jQuery.isReady) { // Remember that the DOM is ready jQuery.isReady = true; // If there are functions bound, to execute if (jQuery.readyList) { // Execute all of them jQuery.each(jQuery.readyList, function () { this.call(document); }); jQuery.readyList = null; } } } }); var readyBound = false; //判斷是否加載完成的函數 可以剝離出來使用 function bindReady() { if (readyBound) return; readyBound = true; // Mozilla, Opera (see further below for it) and webkit nightlies currently support this event if (document.addEventListener && !jQuery.browser.opera) // Use the handy event callback document.addEventListener("DOMContentLoaded", jQuery.ready, false); // If IE is used and is not in a frame // Continually check to see if the document is ready if (jQuery.browser.msie && window == top)(function () { 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 (error) { setTimeout(arguments.callee, 0); return; } // and execute any waiting functions jQuery.ready(); })(); if (jQuery.browser.opera) document.addEventListener("DOMContentLoaded", function () { if (jQuery.isReady) return; for (var i = 0; i < document.styleSheets.length; i++) if (document.styleSheets[i].disabled) { setTimeout(arguments.callee, 0); return; } // and execute any waiting functions jQuery.ready(); }, false); if (jQuery.browser.webkit) { var numStyles; (function () { if (jQuery.isReady) return; if (document.readyState != "loaded" && document.readyState != "complete") { setTimeout(arguments.callee, 0); return; } if (numStyles === undefined) numStyles = jQuery("style, link[rel=stylesheet]").length; if (document.styleSheets.length != numStyles) { setTimeout(arguments.callee, 0); return; } // and execute any waiting functions jQuery.ready(); })(); } jQuery.event.add(window, "load", jQuery.ready); } //常用事件添加 例如可以使用$().click(function(){}); 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) : null; }; }); //jQuery ajax包裝 jQuery.extend({ //HTTP get方法 get: function (url, data, callback, type) { // shift arguments if data argument was ommited if (jQuery.isFunction(data)) { callback = data; data = null; } return jQuery.ajax({ type: "GET", url: url, data: data, success: callback, dataType: type }); }, //HTTP POST方法 post: function (url, data, callback, type) { if (jQuery.isFunction(data)) { callback = data; data = {}; } return jQuery.ajax({ type: "POST", url: url, data: data, success: callback, dataType: type }); }, //獲得JSON數據 getJSON: function (url, data, callback) { return jQuery.get(url, data, callback, "json"); }, //設置全局 AJAX 默認選項。 ajaxSetup: function (settings) { jQuery.extend(jQuery.ajaxSettings, settings); }, ajaxSettings: { url: location.href, type: "GET", timeout: 0, contentType: "application/x-www-form-urlencoded", processData: true, async: true, data: null, username: null, password: null, accepts: { xml: "application/xml, text/xml", html: "text/html", json: "application/json, text/javascript", text: "text/plain", _default: "*/*" } }, //為下一次請求緩存Last-Modified頭部. lastModified: {}, ajax: function (s) { //兩次繼承s s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); var status, data, type = s.type.toUpperCase(); //我把jsonp和script取消了,可惜了 可以直接在頁面上插入script來完成跨域 // If data is available, append data to url for get requests if (s.data && type == "GET") { s.url += (s.url.match(/\?/) ? "&" : "?") + s.data; // IE likes to send both get and post data, prevent this s.data = null; } var requestDone = false; //比較簡單的創建請求對象;微軟在IE7上並沒有正確地實現XMLHttpRequest var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); // Open the socket // 只是說open用於初始化一個准備發起仍在'pending'狀態中的請求 if (s.username) xhr.open(type, s.url, s.async, s.username, s.password); else xhr.open(type, s.url, s.async); // Need an extra try/catch for cross domain requests in Firefox 3 try { //// 如果需要一個過期頭, 那就設置這個過期頭.過期頭所標識的日期一般用於瀏覽器的緩存設置. if (s.ifModified) xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT"); // 設置頭部,以便能使服務器知道這是一個通過XMLHttpRequest發送的請求. xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); //設置接收數據的類型 xhr.setRequestHeader("Accept", s.dataType && s.accepts[s.dataType] ? s.accepts[s.dataType] + ", */*" : s.accepts._default); } catch (e) {} //等待返回的請求 var onreadystatechange = function (isTimeout) { // The transfer is complete and the data is available, or the request timed out if (!requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout")) { requestDone = true; // clear poll interval if (ival) { clearInterval(ival); ival = null; } status = isTimeout == "timeout" && "timeout" || !jQuery.httpSuccess(xhr) && "error" || s.ifModified && jQuery.httpNotModified(xhr, s.url) && "notmodified" || "success"; if (status == "success") { // Watch for, and catch, XML document parse errors try { // process the data (runs the xml through httpData regardless of callback) data = jQuery.httpData(xhr, s.dataType, s.dataFilter); } catch (e) { status = "parsererror"; } } //如果設置了ifModified為true,對響應頭進行緩存 //下次請求相同url的時候可以看看請求的頁面的修改日期是否晚過這個日期 if (status == "success") { // Cache Last-Modified header, if ifModified mode. var modRes; try { modRes = xhr.getResponseHeader("Last-Modified"); } catch (e) {} // swallow exception thrown by FF if header is not available if (s.ifModified && modRes) jQuery.lastModified[s.url] = modRes; success(); } else jQuery.handleError(s, xhr, status); // 觸發complete事件, 運行綁定在這個事件上事件監聽函數 complete(); // 把xhr設為null, 讓垃圾回收器對xhr進行回收,防止內存泄漏 if (s.async) xhr = null; } }; //如果是異步的請求, 設置請求重試, 一次不成功就再來直到成功或者超時 if (s.async) { // don't attach the handler to the request, just poll it instead var ival = setInterval(onreadystatechange, 13); // Timeout checker if (s.timeout > 0) setTimeout(function () { // 如果xhr不為null, 說明請求正在進行,取消這次請求, 因為超時了 if (xhr) { // Cancel the request xhr.abort(); //如果請求還沒完成,馬上調用函數這樣requestDone為true if (!requestDone) onreadystatechange("timeout"); } }, s.timeout); } //發送請求 try { xhr.send(s.data); } catch (e) { jQuery.handleError(s, xhr, null, e); } //在firefox 1.5中,同步請求並不能觸發statechange事件.所以手動觸發 if (!s.async) onreadystatechange(); function success() { // If a local callback was specified, fire it and pass it the data if (s.success) s.success(data, status); } function complete() { // Process result if (s.complete) s.complete(xhr, status); } // return XMLHttpRequest to allow aborting the request etc. return xhr; }, //jQuery.ajax方法中出現的錯誤處理函數 handleError: function (s, xhr, status, e) { // If a local callback was specified, fire it if (s.error) s.error(xhr, status, e); }, //判斷當前這個請求是否是成功 httpSuccess: function (xhr) { try { // IE有一個錯誤, 那就是有時候應該返回204(No Content)但是它卻返回1223 //safari在文檔沒有修改時(304)得到的status會等於undefined,所以把這種情況也當作是成功 return !xhr.status && location.protocol == "file:" || (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || xhr.status == 1223 || jQuery.browser.webkit && xhr.status == undefined; } catch (e) {} return false; }, //判斷請求回來的服務器響應是不是"NotModified". httpNotModified: function (xhr, url) { try { var xhrRes = xhr.getResponseHeader("Last-Modified"); //Firefox 總是返回200. 還是對比一下Last-Modified的日期穩妥一些. return xhr.status == 304 || xhrRes == jQuery.lastModified[url] || jQuery.browser.webkit && xhr.status == undefined; } catch (e) {} return false; }, /*獲取XMLHTTPRequest的響應數據.允許對數據使用自定義的函數進行預處理.並根據用戶提供的數據類型對響應數據做不同的處理. 最后將數據返回.*/ httpData: function (xhr, type, filter) { var ct = xhr.getResponseHeader("content-type"), xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0, data = xml ? xhr.responseXML : xhr.responseText; if (xml && data.documentElement.tagName == "parsererror") throw "parsererror"; // Get the JavaScript object, if JSON is used. if (type == "json") data = eval("(" + data + ")"); return data; } }); // 使用jQuery的原型覆蓋init的原型 這樣就自動把jQuery.prototype的方法可以在init里面使用 jQuery.fn.init.prototype = jQuery.fn; })(window);