整個封裝過程及閱讀JQuery源碼的過程基本上持續了一個月吧,最終實現了一個大概30%的JQuery功能的框架版本,可是裏面涉及的知識點也是很是多的,總共的代碼加上相關的註釋大概在3000行左右吧,但也只是對JQuery的裏面的知識點了解了大概,後續但願能更深層次的理解JQuery裏面涉及的知識點,從而寫出高質量的代碼,特此記錄一下這一段時間學習的點滴。javascript
/* * @Author: 我愛科技論壇 * @Time: 20180722 * @Desc: 實現一個相似於JQuery功能的框架 * V 1.0: 實現了基礎框架、事件框架、CSS框架、屬性框架、內容框架、動畫框架的搭建 * V 2.0:實現了框架的進一步優化,具備良好的擴展性, 能夠支持鏈式訪問 * V 3.0:種子模塊:命名空間、對象擴展、數組化、類型的斷定、domReady,無衝突處理 * V 4.0: 數據類型的檢測、正則表達式的基本用法,數據綁定的知識(模板的使用) * V 5.0:實現繼承的基本理解,事件框架的封裝和測試 * V 6.0: 實現了CSS樣式框架封裝和測試 * V 7.0: 實現了選擇框架的封裝和測試 * V 8.0: 實現了屬性框架的封裝和測試 * V 8.0: 實現了DOM框架的封裝和測試 * V 9.0:實現了本地存儲框架的封裝(緩存框架、cookie、Localstorage框架) * V 10.0 實現了動畫框架的封裝和本地測試 * V 11.0 在每個當即函數+閉包的前面添加;實現框架的兼容性 * */
/** * 用於給js中內置的對象進行擴充方法 */ ;(function () { // 爲了使得下面定義的擴充函數執行,祥和裏須要調用一下 stringExtend() arrayExtend() functionExtend() // String對象方法的擴充 function stringExtend() { // str = 'name: @(name), age:@(age)' // data = {name : 'xiugang', age : 18} /** * 實現一個簡單的數據綁定 * @param str * @param data * @return {*} */ String.prototype.formateString = function (data) { return this.replace(/@\((\w+)\)/g, function (match, key) { // 注意這裏找到的值必須返回出去(若是是undefined,就是沒有數據) // 注意:判斷一個值的類型是否是undefined,能夠經過typeof判斷 console.log(typeof data[key] === 'undefined'); return data[key] === 'undefined' ? '' : data[key]; }); } /** * 去掉座標的空格 * @param str * @return {*} */ String.prototype.ltrim = function () { return this.replace(/^\s*/g, ''); } /** * 去掉右邊的空格 * @param str * @return {*} */ String.prototype.rtrim = function () { return this.replace(/\s*$/g, ''); } /** * 去掉兩邊的空格 * @param str * @return {*} */ String.prototype.trim = function () { return this.replace(/(^\s*)|(\s*$)/g, ''); } // red===>Red /** * 將第一個字母小寫,其餘字母大寫 * @param str * @return {*} */ String.prototype.camelCase = function () { // .*?是非貪婪的匹配,點能夠匹配任意字符,星號是前邊的字符有0-n個均匹配,問號是則是0-1; // (^\w{1}): 用於匹配第一個首字母 // (.*):用於匹配任意個的前面的字符 // - param 1: 匹配到的字符串 // - param 2: 匹配的的子字符串 // - param 3: 匹配的子字符串 // - param 4: 匹配到的字符串在字符串中的位置 // - param 5: 原始字符串 return this.replace(/(^\w{1})(.*)/g, function (match, g1, g2) { return g1.toUpperCase() + g2.toLowerCase(); }); } /** * 將一個字符串的下劃線轉換爲中劃線 * @param str * @return {*} */ String.prototype.dashString = function () { // 這裏面的this實際上指向的就是咱們本身定義的一個變量字符串 return this.replace(/\_/g, '-'); } /** * 檢測一個字符串是否是爲空 * @return {boolean} */ String.prototype.isEmpty = function () { return this.length === 0; } /** * 判斷字符串是否是包含一個字符串 * @param target * @return {boolean} */ String.prototype.contains = function (target) { // 只要這個indexOf的下標不是-1的話,就說明包含這個目標字符串,不然的話就是不包含 // indexOf() 方法可返回某個指定的字符串值在字符串中首次出現的位置,若是沒找到的話,就返回-1 return this.indexOf(target) !== -1; } /** * 對一個字符串中的特殊字符進行轉義 * @return {string} */ String.prototype.escapeHTML = function () { /*顯示結果 描述 實體名稱 實體編號 空格   < 小於號 < < > 大於號 > > & 和號 & & " 引號 " " ' 撇號 ' (IE不支持) ' ¢ 分 ¢ ¢ �0�5 鎊 £ £ �0�6 日圓 ¥ ¥ € 歐元 &euro * **/ // 先進行字符串分割, 獲得一個數組 var strArr = this.split(''); for (var pos = 0, l = strArr.length, tmp; pos < l; pos++) { // 拿到數組中的每個元素 tmp = strArr[pos]; // 對字符串中的每個元素進行判斷, 若是是特殊字符的話就進行處理 switch (tmp) { // pos始終爲1, 表示要替換的項是1項 case '<': replaceArr(strArr, pos, '<'); break; case '>': replaceArr(strArr, pos, '>'); break; case '\'': replaceArr(strArr, pos, '''); break; case '\"': replaceArr(strArr, pos, '"'); break; case '&': replaceArr(strArr, pos, '&'); break; default: ; } } // join() 方法用於把數組中的全部元素放入一個字符串。 return strArr.join(''); // 專門用於替換掉數組中的元素 /** * 替換數組中指定的項 * @param arr * @param pos * @param item * @return {*} */ function replaceArr(arr, pos, item) { // Splice: splice主要用來對JS中的數組進行操做,包括刪除,添加,替換等,原來的數組會被改變 // 刪除數據:array.splice(index,num),返回值爲刪除內容,array爲結果值。index爲起始項,num爲刪除元素的的個數。 // 插入數據:array.splice(index,0,insertValue),index要插入的位置,insertValue要插入的項 // 替換數據:array.splice(index,num,insertValue),index起始位置,num要被替換的項數,insertValue要替換的值 return arr.splice(pos, 1, item); } } /** * 忽略HTML中的一些內置的特殊字符 * @return {string} */ String.prototype.escapeHTML = function () { return Array.prototype.slice.call(this).join('').replace(/$/g, '&') .replace(/\</g, '<') .replace(/\>/g, '>') .replace(/\'/g, ''') .replace(/\"/g, '"'); } /** * 對字符串進行反轉義 * @return {string} */ String.prototype.unescapeHTML = function () { // 因爲這裏的this實際上拿到的是一個字符串數組, 所以第一步須要先把字符串數組轉換爲一個字符串 console.log(typeof this); // 1.先把這個僞數組轉換爲數組對象 var arr = Array.prototype.slice.call(this); // 2.把數組中的內容轉換爲字符串 var res = arr.join(''); // 查找全部的< > & " ' 字符,並替換掉 return res.replace(/</g, '<') .replace(/>/g, '>') .replace(/'/g, '\'') .replace(/"/g, '\"') .replace(/&/g, '') // String.fromCharCode() 靜態方法根據指定的 Unicode 編碼中的序號值來返回一個字符串。String.fromCharCode(65,66,67) 「ABC」 .replace(/&#(\d+)/g, function ($0, $1) { //parseInt() 函數將給定的字符串以指定基數(radix/base)解析成爲整數。就是 你想把string當成radix進制數解析成10進制 return String.fromCharCode(parseInt($1, 10)); }); } /** * 把一個字符串進行反轉操做 * @return {string} */ String.prototype.reverse = function () { // 1. 先得到我須要的字符串,而後進行分割處理 var arr = this.toString().split(''); // 2. 對我分割後獲得的數組元素進行逆序處理 arr = arr.reverse(); // 3.把數組中的元素變爲一個字符串 return arr.join(); //return (this.toString()).split('').reverse().join(); } } // Array對象方法的擴充 function arrayExtend() { /** * 將一個數組元素清空 * @return {Array} */ Array.prototype.clear = function () { this.length = 0; return this; } /** * 計算一個數組的長度 * @return {*} */ Array.prototype.size = function () { return this.length; } /** * 返回數組裏面的第一個元素 * @return {*} */ Array.prototype.first = function () { return this[0]; } /** * 返回數組的最後一個元素 * @return {*} */ Array.prototype.last = function () { return this[this.length - 1] } function cacl(arr, callback) { // 變量的初始化(治理在使用的時候進行初始化) var ret; for (var i = 0, len = arr.length; i < len; i++) { ret = callback(arr[i], ret); } return ret; } /** * 對數組的全部元素進行求和 * @return {*} */ Array.prototype.sum = function () { // 1. 通常的方法 /*var ret = 0; for (var i = 0, len = this.length; i < len; i++){ ret = ret + this[i]; } return ret;*/ // 2.使用上面的計算類 /** * @param:item 數組的每一項 * @param:sum 數組求和的結果 */ return cacl(this, function (item, sum) { // 若是剛開始沒有初始化的話,就直接使用第一項做爲sum(ret)的初始值 if (typeof sum === 'undefined') { return item; } else { return sum += item; } }) } /** * 找出數組中的最大值 * @return {*} */ Array.prototype.max = function () { // 1. 通常的方式求出最大值 /*var ret = 0; for (var i = 0, len = this.length; i < len; i++){ if (ret < this[i]){ ret = this[i]; } } return ret;*/ // 2. 第二種方式 return cacl(this, function (item, max) { if (typeof max === 'undefined') { return item; } else { if (max < item) { return item; } else { return max; } } }) } /** * 找出一個數組中的最小值 * @return {*} */ Array.prototype.min = function () { return cacl(this, function (item, min) { if (typeof min === 'undefined') { return item; } else { // 只要每一項的值都不比最小值小的話 if (!(min < item)) { return item; } else { return min; } } }) } /** * 求出一個數組中全部元素的平均值 * @return {*} */ Array.prototype.avg = function () { // 1. 先對數組中的元素個數組進行判斷一下,防止計算出現無窮的狀況 if (this.length === 0) { return; } var sum = this.sum(); return sum / this.length; /*return cacl(this, function (item, avg) { // 1. 先求和(進入到這個函數裏面, this指向的是window對象,此時window對象是沒有sum方法的,故執行錯誤) //var sum = this.sum(); // 2.求出平均值 if (typeof avg === 'undefined'){ return item; } else{ avg = sum / (this.length); } return avg; })*/ } // 去除數組中的重複項 /* * 實現思路: 遍歷原始數組中的每一項元素,讓每次遍歷的這一個元素和後面的每個元素進行比較 * 【只要相同的話就直接跳過繼續向下尋找】 * */ Array.prototype.unique = function () { var a = [], len = this.length; for (var i = 0; i < len; i++) { for (var j = i + 1; j < len; j++) { if (this[i] === this[j]) { // 若是找到了相鄰的兩個元素是相同的,i直接向後移動一位 // 而後j開始從i的位置繼續向後尋找元素 j = ++i; } } a.push(this[i]); } ; return a; } /** * 去除數組中的重複項 * 【實現思路】:先對數組進行排序,而後比較相鄰的元素是否相同 * @return {Array} */ Array.prototype.unique = function () { var tmp = [], len = this.length; // 1.先對原始的數組進行排序 this.sort(); // 2.比較相鄰的元素 for (var i = 0; i < len; i++) { // 只要相鄰的元素相同,就直接跳過 if (this[i] === this[i + 1]) { continue; } // 因爲tmp.length初始的位置一直是0, 添加一個元素以後變爲1,所以下標和長度每次相差1, 實現了實時插入數據的功能 tmp[tmp.length] = this[i]; } return tmp; } /** * 實現兩個數組的並集,而後去除重複元素 * @param target * @return {*} */ Array.prototype.union = function (target) { // concat() 方法用於鏈接兩個或多個數組。 // 鏈接數組以後而後去除數組中的重複項 return this.concat(target).union(); } /** * 求出兩個數組的交集 * @param target * @return {Array|*[]} */ Array.prototype.intersect = function (target) { // 1.先去除原始數組和目標數組中的重複元素 var originArr = this.unique(), targetArr = target.unique(); // filter()的做用是返回某一數組中知足條件的元素,該方法返回的是一個新的數組 // 2.開始使用條件過濾 /** * @param element(必選):當前元素的值 @param index(可選): 當前元素的索引 @param array(可選):當前元素所屬的數組 */ return originArr.filter(function (element, index, array) { // filter函數默認會把全部的返回false的元素去掉 for (var i = 0, len = targetArr.length; i < len; i++) { if (element === targetArr[i]) { // 只要是返回知足true的全部條件,基本上都會被過濾掉 return true; } //return false; } // 只有找到相同的元素的時候返回的是true,其餘狀況都是返回的是false return false; }); } /** * 找出兩個數組中的不一樣元素 * @param target * @return {Array|*[]} */ Array.prototype.diff = function (target) { // 1. 獲取原始數組和目標數組,去除重複項 var orignArr = this.unique(), targetArr = target.unique(); // 2. 開始使用filter函數過濾條件 return orignArr.filter(function (element, index, array) { for (var i = 0, len = targetArr.length; i < len; i++) { // 只要元素相等的話,就所有過濾掉 if (element === targetArr[i]) { return false; } } return true; }); } /** * 對數組的每一項遍歷的時候設置一個回調函數(沒有返回結果) * @param fn * @param ctx */ Array.prototype.forEach = function (fn, ctx) { var i = 0, len = this.length; for (; i < len; i++) { // element, index, array // call 的第一個參數也就是this的指向, 其餘參數表示須要傳遞給回調函數的的參數 fn.call(ctx || null, this[i], i, this); } } /** * * 對數組的每一項執行回調,返回由回調函數的結果組成的數組 * @param fn * @param ctx * @return {Array} */ Array.prototype.map = function (fn, ctx) { // 初始化變量 var ret = [], i = 0, len = this.length; // 遍歷數組的每一項元素, 返回由回調函數的結果組成的數組 for (; i < len; i++) { // 調用回調函數, 返回指向結果 res = fn.call(ctx || null, this[i], i, this); // 將每一項執行的結果放入到一個新的數組裏面 ret.push(res); } return ret; } /** * 對數組的每一項執行回調函數, 返回回調函數執行結果爲true的數組集合 * @param fn * @param ctx */ Array.prototype.filter = function (fn, ctx) { var ret = [], i = 0, len = this.length; // 遍歷每一項,把執行結果爲true的全部元素集合存起來 for (; i < len; i++) { // 注意這裏的這種運算方式只會返回全部的回調函數返回true的計算結果集 fn.call(ctx || null, this[i], i, this) && ret.push(this[i]); } return ret; } /** * 遍歷數組中的每一項元素 * @param fn */ Array.prototype.each = function (fn) { var i = 0, len = this.length; for (; i < len; i++) { fn.call(this[i]); } } /** * 對數組的【每一項】執行回調函數,必須每一項回調函數返回true, 就返回true * @param fn * @param ctx */ Array.prototype.every = function (fn, ctx) { var i = 0, len = this.length; // 遍歷數組中全部的元素, 只要有一個函數回調函數爲false就返回false,只要全部的都是true纔會返回true for (; i < len; i++) { // 如:a默認是undefined,!a是true,!!a則是false,因此b的值是false,而再也不是undefined。這樣寫能夠方便後續判斷使用。 // 因此,!!(a)的做用是將a強制轉換爲布爾型(boolean)。 // 若是a = null, !!(a) 的結果就是假, 能夠直接把一個弱類型強制轉換爲一個新的類型 // 下面的代碼就是強制將一個函數轉換爲bool的類型 if (!!fn.call(ctx || null, this[i], i, this) === false) return false; // 上面的代碼等價於 /*if (fn.call(ctx || null, this[i], i, this)) { return true; }*/ } return true; } /** * 對數組中的每一項執行回調函數,只要有一項爲true的話,就是true,不然就是false * @param fn * @param ctx */ Array.prototype.some = function (fn, ctx) { var i = 0, len = this.length; // 循環遍歷每一項,只要有一項爲true,就是true for (; i < len; i++) { /* * // 強制轉換爲Boolean 用 !! var bool = !!"c"; console.log(typeof bool); // boolean // 強制轉換爲Number 用 + var num = +"1234"; console.log(typeof num); // number // 強制轉換爲String 用 ""+ var str = ""+ 1234; console.log(typeof str); // string * */ if (!!fn.call(ctx || null, this[i], i, this) === true) return true; } return false; } /** * 從左向右執行回調函數(第二個元素開始) * 其中包含了上一次回調的返回值 * @param callback */ Array.prototype.reduce = function (callback) { var i = 0, len = this.length, callbackRet = this[0]; // 這個變量保存着上一次回到的函數的返回結果, 默認存儲的是第一個元素 for (; i < len; i++) { // this的指向,element, index, 數組對象自己 // callbackRet 裏面存儲了數組上一次計算的處理結果 callbackRet = callback.call(null, callbackRet, this[i], i, this); } return callbackRet; } /** * 從右向左處理每一項元素,倒數第二項開始執行 * @param callback */ Array.prototype.reduceRight = function (callback) { var len = this.length, i = this[len - 2], callbackRet = this[len - 1]; // 保存着最後一項 // 從倒數第二項開始向前遍歷數組的每一項 for (; i >= 0; i--) { //this指向, prev, element, index, arr callbackRet = callback.call(null, callbackRet, this[i], i, this); } return callbackRet; } /** * 返回目標值target在數組中第一次出現的位置, 搜索默認會從左向右執行 * @param target * @param start */ Array.prototype.indexOf = function (target, start) { /* * 實際上是一種利用符號進行的類型轉換,轉換成數字類型 ~~true == 1 ~~false == 0 ~~"" == 0 ~~[] == 0 ~~undefined ==0 ~~!undefined == 1 ~~null == 0 ~~!null == 1 * */ var len = this.length, start = ~~start; // 若是start不傳過來,這裏就是undefined,指向後面的就會保存,這裏使用了~~把其餘類型強制轉換爲數字類型 if (start < 0) { // 若是指定搜索的起始位置小於0的話, 默認就從0的位置開始向後搜索 start = 0; } // 從用戶指定的起始位置開始向後搜索 for (; start < len; start++) { if (this[start] === target) { return start; } } // 若是沒找到的話,就返回-1 return -1; } /** * 返回指定的目標值在數組中最後一次出現的位置 * @param target * @param start */ Array.prototype.lastIndexOf = function (target, start) { // 這裏至關因而typeof start ==== 'undefined' if (start === void 0) { start = this.length; } else if (start < 0) { start = 0; } // 開始從數組的最後面向前遍歷 for (; start >= 0; start--) { // 找到目標元素target在數組中最後一次出現的位置(從後向前找) if (this[start] === target) { return start; } } return -1; } /** * 數組去重方法增強版本 * 侷限性:只適用於數組中存放的是單一的數據類型,若是是多種數據類型並存的話,就會去重失敗 * ['ff', 1, '1'] */ Array.prototype.enhanceUnique = function () { var ret = [], tempMap = {}, i = 0, len = this.length, temp; // 遍歷數組的每一項 for (; i < len; i++) { temp = this[i]; // 只要這個tempMap中沒有這一項的話,就直接放入到數組中去 if (tempMap[temp] === void 0) { ret.push(temp); // {}數據的存儲格式爲{1 : true, 2 : false, 3 : false} tempMap[temp] = true; } } return ret; } /** * 刪除數組中的指定元素, 經過arguments僞數組的方式來接受傳遞過來的參數 * 通過測試,只能刪除數組中重複的多餘的元素 * @return {Array} */ Array.prototype.without = function () { // slice(start, end) 方法可從已有的數組中返回選定的元素。 // 若是slice()這個函數沒有指定結束的位置的話,默認是會返回數組中的start以後的全部元素 // 1. 獲取用戶傳過來的參數, 去掉數組中重複的元素 //var args = [].slice.call(arguments).unique(); /* * Array.prototype.slice.call({ 0:"likeke", 1:12, 2:true, length:3 }); * */ //1. 因爲arguments其實是一個僞數組,不能直接使用數組裏面的方法 // 所以先要把arguments轉換爲數組 var arr = Array.prototype.slice.call(arguments) || [].slice.call(arguments); // 2. 把數組中的重複元素去重 var args = arr.unique(), len = this.length, aLength = args.length, i = 0, j = 0; // 遍歷原始的數組(因爲後面每次刪除掉一個元素以後,這裏的this.length的長度就是已經都改變了, 所以每次在執行完畢以後都要從新計算一下length) for (; i < len; i++) { for (; j < aLength; j++) { if (this[i] === args[j]) { // 只要刪除的數組在個人這個裏面,就直接去掉 // i 爲起始的值,1爲要刪除的項, 也就是刪除i位置的元素 // splice 返回的是刪除的元素, this內容是已經修改過以後的項 this.splice(i, 1); // 爲了不刪除數組的元素以後的數組長度的變化,這裏須要從新計算一下數組的新的長度 // len = this.length; } } // 將j下標復位,以便下一次循環(注意是在每一次j循環完畢以後而後再把j初始化到原始的狀態) j = 0; } return this; } /** * 去掉數組中的目標元素 */ Array.prototype.enhanceWithout = function () { // 用於去除數組中指定的的多餘的元素 var ret = [], len = this.length, args = ([]).slice.call(arguments), argsLength = args.length, i = 0, j = 0; for (; i < len; i++) { for (; j < argsLength; j++) { if (args[j] !== this[i]) { ret.push(this[i]); } } // 因爲這裏的j使用的是局部變量,所以這裏須要進行處理 j = 0; } return ret; } /** * 實現一個數組的扁平化(能夠解決數組裏面存放數組的問題)【遞歸處理調用】 * [[], [], [], [[], [], []]] * @return {Array} */ Array.prototype.flatten = function () { // 實現一個flatten函數,將一個嵌套多層的數組 array(數組) (嵌套能夠是任何層數)轉換爲只有一層的數組 // 數組中元素僅基本類型的元素或數組, var ret = [], len = this.length, // 注意當下一次執行遞歸調用以後,這裏的this指向的是tmp i = 0, tmp; for (; i < len; i++) { // 注意這裏先取出來數組中的每一項元素 tmp = this[i]; // 判斷一下數組裏面存放的仍是不是數組類型(數組裏面的每一項) if (({}).toString.call(tmp) === '[object Array]' || Object.prototype.toString.call(tmp) === '[object Array]') { // 繼續遞歸調用(遞歸調用的時候須要把結果存起來哦) // 1. 對當前數組裏面的數組進行扁平化處理, tmp.flatten()獲得的就是一個普通的數組類型 // 2. 因爲ret是一個數組類型,使用concat以後能夠把兩個數組裏面的元素連接起來 // 下一次執行遞歸的時候上面的this就是指向了這裏的tmp數組 ret = ret.concat(tmp.flatten()) //tmp.flatten(); } else { // 若是不是數組類型的話,就直接放入到個人新數組裏面 ret.push(tmp); } } return ret; } /** * 刪除數組中的指定位置的項 * @param pos * @return {Array} */ Array.prototype.removeAt = function (pos) { // 移出數組中指定位置的項 // slice() 函數調用的執行結果返回的是刪除掉的項, 這個this就是修改以後的項 this.splice(pos, 1); return this; } /* 【經驗話語1】 直接用等號 (==) 判斷時,變量必需要聲明(包括不用var 的隱式聲明),不然出錯。 無論變量有沒有聲明,均可用typeof 判斷,注意typeof 返回結果爲字符串,因此是與"undefined"作比較。 因此,判斷類型最好用typeof ,由於當判斷的變量是在其餘js 文件中定義的全局變量時, 執行此判斷時,定義該變量所在的js 文件可能還未加載完成,用== 判斷就會報錯:is not defined 【經驗話語2】 注意slice()和splice() 這二者的區別 * */ /** * 檢測數組中是否是包含某一項 * @param target * @return {boolean} */ Array.prototype.contains = function (target) { // 能夠調用本身以前申明好的some方法,數組中只要有一項,就會返回true return this.some(function (element, index, self) { // 調用this.some()方法實際上會返回遍歷數組元素的每一項 return element === target; }) } /** * 隨機返回數組中的某一項(把數組中的任意一項返回) * @param n * @return {*} */ Array.prototype.random = function (n) { //Math.floor():向下取整。Math.floor(1.8) -> 1 //Math.ceil():向上取整。Math.ceil(1.1) -> 2 //v = Math.random() * n:會產生一個 0 < v < nv的數 //v2 = Math.floor(Math.random() * n):v2爲一個大於等於0,小於n的整數 var index = (Math.floor(Math.random() * n)); return this[index] || this[this.length - 1]; } } // Function對象方法的擴充 function functionExtend(func) { Function.prototype.before = function (func) { // 通常來講加下劃線的變量爲私有變量,這是常規都比較遵照的一種代碼規範。 var __self = this; // 私有的屬性用下劃線 return function () { // 從新把我須要傳遞的參數傳遞過去, 若是目標函數返回的是false, 就是false if (func.apply(this, arguments) === false) { return false; } // 不然就把個人本身的參數傳遞過去 return __self.apply(this, arguments); } } /** * AOP 切面編程的函數擴充 * @param func * @return {Function} */ Function.prototype.after = function (func) { var __self = this; return function () { var ret = __self.apply(this, arguments); // //返回一個函數,至關於一個代理函數,也就是說,這裏包含了原函數和新函數,原函數指的是myFunc,新函數指的是fn if (ret === false) { return false; } func.apply(this, arguments); return ret; } } } })(); // 主框架: 只作一件事,就是用於獲取全部的元素集合 ;(function (w) { // 定義一個Xframe對象,後面就是他的構造函數 var xframe = function (selector, context) { // 爲了使得後面的函數this始終指向的是xframe框架,這裏須要修改函數內部this的指向 return this.init.apply(this, [selector, context]); }; // 定義一個初始化函數,用於初始化獲取全部的元素集合 // 只要用戶使用了相似於JQuery中的選擇元素的方法,就開始選擇一個元素集合 // 這裏的核心功能:其實是爲了使用一個僞數組實現一個相似於JQuery中的鏈式訪問的功能 xframe.prototype.init = function (selector, context) { // 開始構建一個僞數組:{1 : list[0], 2 : list[1], , , , length : list.length} this.length = 0; // 針對沒有參數的處理方式 if (typeof selector === 'undefined') { return this; } if (typeof selector === 'string') { var nodeList = (context || document).querySelectorAll(selector); this.length = nodeList.length; for (var i = 0, len = this.length; i < len; i++) { this[i] = nodeList[i]; } } else if (selector.nodeType) { // 若是獲取的是一個元素節點,文本節點,或者屬性節點的話 this[0] = selector; this.length++; } // 爲了能夠支持鏈式訪問必須把這個this對象返回出去 return this; }; // 使用雙對象法則繼續暴露出去一個對象,進行對象的二次封裝 // 【雙對象法則的使用】 var $$ = function (selector, context) { // 這裏使用一個簡單的異步加載機制,等待全部的DOM元素執行完畢以後再開始繼續向下執行 if (typeof selector === 'function') { // selector就是DOM元素加載完畢以後的繼續向下執行的回調函數 //w.onload = selector; // 使用本身定義的函數來實現一個domReady(ele, fn)的功能, 默認就是整個document加載完畢以後纔會繼續向下執行 // 使用call的時候第一個參數不能少哈, 不然傳過去的參數就是空的 //$$.onDOMReady.call(this, selector); // 使用apply傳參的時候必須傳遞的是一個數組類型 //$$.onDOMReady.apply(this, [selector]); // 若是使用bind的話(只是會修改調用函數內部的指向, 可是不會調用) // bind 是返回對應函數,便於稍後調用;apply 、call 則是當即調用 。【只是會返回一個函數, 但仍是不會當即調用】 var func = $$.onDOMReady.bind(this, selector); // 調用使用bind()方法返回的函數 func(); } else { // 若是不是一個函數的話 return new xframe(selector, context); } } // 添加一個extend方法, 用於擴充一個對象的方法, 擴展向一個類中拷貝方法 $$.extend = function () { // 這裏須要分爲兩種狀況: // 1. 若是傳過來的是一個參數的話,就至關因而給xframe對象添加方法 // 2. 若是是兩個參數的話,就至關因而給一個類擴充方法(把一個函數的方法拷貝到另外一個類中去) var len = arguments.length, target = null, // target 用來存儲須要把方法拷貝進去的目標函數 i = 1, // 初始化變量i, 表示須要開始遍歷的起始位置標記 key; // 爲了防止定義太多的局部變量,能夠把後面須要用到的全部局部變量事先在前面定義好 if (len === 0) { return; } else if (len === 1) { // 給xrame對象添加方法 target = xframe.prototype; i--; } else { // 兩個參數的話,那麼第一個參數就是我須要拷貝新的方法進去的目標對象 // 若是是兩個參數的話:就不須要修改變量i的值了, 直接從第一個位置開始,拿到第一個參數後, 把第二個參數的方法所有拷貝給第一個對象 // 注意: 這裏有可能也是三個參數或者是多個參數, 所以也能夠吧後面的好幾個對象的屬性或者方法添加給第一個對象 target = arguments[0]; } // 肯定好了target 這個目標對象之後,開始遍歷原始對象那個source,把source裏面的方法所有都拷貝到這個target對象裏面 for (; i < len; i++) { // 這裏實際上在遍歷一個json對象,json對象的每一項實際上就是一個屬性或者方法 // 遍歷每個arguments, 獲取每個參數的屬性, 而後把這個屬性拷貝到原始的對象 for (key in arguments[i]) { target[key] = arguments[i][key]; } } return target; } // 爲了把主框架裏面的局部變量暴露出去供其餘模塊使用 w.xframe = w.$ = $$; })(window); // 公共框架 // 種子模塊:命名空間、對象擴展、數組化、類型的斷定、domReady機制,無衝突處理 ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法) xframe.extend({ // 版本1:從前向後遍歷 each: function (fn) { var i = 0, len = this.length; for (; i < len; i++) { // call第一個參數傳遞的實際上就是this的執行,後面的參數就是目標函數fn須要傳遞的參數(可省略) // this[i] 裏面的取值方式相似於json取值,每個參數存儲了選擇器獲取的全部的nodeList元素集合中的一個元素 fn.call(this[i]); } return this; }, // 版本2: 從後向前遍歷(建議對於DOM元素通常從後向前開始遍歷) each: function (fn) { var i = this.length - 1; for (; i >= 0; i--) { fn.call(this[i], i); } }, /** * 將一個僞數組轉換爲數組,而後開始遍歷這個集合 * @param pArr * @param fn */ toArray: function (pArr, fn) { var arr = Array.prototype.slice.call(pArr), i = arr.length - 1; for (; i >= 0; i--) { // element index fn.call(arr[i], i); } }, }); // 不須要參與鏈式訪問的 /*公共部分*/ xframe.extend(xframe, {}); /*字符串處理模塊*/ xframe.extend(xframe, { /* * 下面的這幾個都會用到正則表達式,會在後面補充 * camelCase函數的功能就是將形如background-color轉化爲駝峯表示法:backgroundColor * */ camelCase: function (str) { // all: -c, letter: c return str.replace(/\-(\w)/g, function (all, letter) { // 把全部的字母都轉換爲大寫的狀態 return letter.toUpperCase(); }); }, /** * 去掉左邊的空格 str = ' ()' * @param str * @returns {*} */ ltrim: function (str) { /* ^ :表示以XX開頭 \s: 表示空格 *: 表示匹配零個或者多個 g: 表示匹配所有,若是沒有的話默認只會匹配一個 (^\s*): 表示以空格開頭的一個或者多個字符 str.replace(, ''): 替換…… ----------------------------------------------------[其餘用法概括]------------------------------------- ^, $: 匹配字符串開始,結束的位置 eg: g, i:匹配全部,不區分大小寫的字符串; eg: /a/g, /a/i *, +, ?: 匹配任意次數, 匹配前面的字符一次或者屢次, 0次或者1次 [] : 匹配一個字符集合; eg: [a-z]全部小寫字母的集合, [0-9]全部數字的集合 eg: [a-zA-Z]全部大小寫字母的集合 脫字符^: 匹配任何不在該集合中的字符,與上面的用法正好相反 {}: 指定重複前面的一個字符多少遍 eg:{N} 重複n遍 eg:{n, m}重複n-m遍 eg: {n, }至少重複n遍 eg:{,m}至多重複m遍 // 【熟記:同類記憶法】 \s: 表示空格:包括空格、換行、回車、tab,等價於[\n\r\t\f] \S: 匹配非空格字符,等價於[^ \n\r\t\f] \d: 表示十進制數字,等價於[0-9] \D: 匹配一個非數字字符, 等價於[^0-9] \w(小寫): 表示字母或者數字,等價於[a-zA-Z0-9] \W: 非字母且非數字,與\w相反,等價於:[^a-zA-Z0-9]* * */ return str.replace(/(^\s*)/g, ''); }, /* 去掉右邊的空格, str = '() ' * @param str */ rtrim: function (str) { return str.replace(/(\s*$)/g, ''); }, /** * 用於去掉兩邊的空格(去掉全部的空格) str =' () ' * @param str * @returns {*} */ trimOld: function (str) { return str.replace(/(\s*$)/g, ''); }, /** * 【使用模板來實現一個簡單的數據綁定】 * 實現簡單的數據綁定: @(name), @(sex) * data: var user = {name : 'xiugang', role, '鑽石會員'} * str: = '歡迎@(name), 等級:@(role)光臨本站!'; * @param str 原始的數據格式 * @param data 須要綁定的數據對象,是一個json格式的數據, json = {name : 'xiuxiu', age : 18} * @returns {*} */ formateString: function (str, data) { // 使用後面的值去替換掉前面的值 // 細節分析:((\w+))使用括號匹配的值在JavaScript中實際上就是一個$1, 把這個參數傳給match // (\w+) 第二個括號實際上匹配到的就是一個$2, 把這個參數傳給key // match: @(name), @(age), @(sex) // key: name, age, sex return str.replace(/@\((\w+)\)/g, function (match, key) { // 先判斷有沒有匹配到相應的字符串 // 找到@()開始的字符串, 使用數據域中的數據去替換 // 若是json數據data裏面麼有找到相應的data[key]數據,返回的實際上就是一個空的字符串 return typeof data[key] === 'undefined' ? '' : data[key]; }); }, /** * @param str * @returns {*} */ trimLeft: function (str) { return str.replace(/^\s*/g, ''); }, /** * @param str * @returns {*} */ trimRight: function (str) { return str.replace(/\s*$/g, ''); }, /** * 去掉全部的空格(兩邊的空格), 能夠針對任意格式的字符串 * 先去掉左邊的空格,而後去掉右邊的空格 * @param str * @returns {*} */ trim: function (str) { // var regx = '/^\s*\s*$/g'; // return str.replace(regx, ''); // | 表示或的意思, 也就是知足| 左邊的也成立, 知足 | 右面的也成立 // (^\s*) 表示的就是以0個空格或者多個空格開頭 // (\s*$) 的意思就是, 以0個空格或者多個空格結尾 // /…/g 是正則表達式的屬性, 表示全文匹配, 而不是找到一個就中止 return str.replace(/(^\s*)|(\s*$)/g, ""); //return this.trimRight(this.trimLeft(str)); }, /** * 發送一個ajax請求 * @param url 請求的URL地址信息 * @param fn, 請求成功的回調函數 */ ajax: function (url, fn) { // 建立一個XMLHTTPRequest對象 var xhr = createXHR(); // 每當 readyState 改變時,就會觸發 onreadystatechange 事件。 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // 接受到響應以後,第一步檢查status屬性,爲200則代表成功,此時responseText已經準備就緒; // 爲304代表請求資源未被修改,能夠直接使用瀏覽器中的緩存版本。 if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { fn(xhr.responseText); } else { alert('錯誤的文件!'); } } }; // 定義請求參數, 對於指定的url發送一個get請求 xhr.open('get', url, true); // 發送請求 // 第三個參數:指示請求使用應該異步地執行。 // 若是這個參數是 false,請求是同步的,後續對 send() 的調用將阻塞,直到響應徹底接收。 // 若是這個參數是 true 或省略,請求是異步的,且一般須要一個 onreadystatechange 事件句柄。 xhr.send(); /** * 建立一個XHR */ function createXHR() { //本函數來自於《JavaScript高級程序設計 第3版》第21章 if (typeof XMLHttpRequest != "undefined") { return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined") { // arguments.callee用於指向他的回調函數 if (typeof arguments.callee.activeXString != "string") { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp" ], i, len; for (i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex) { //skip } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available."); } } }, /** * json轉換爲字符串 * @param json * @returns {string} */ json2String: function (json) { return JSON.stringify(json); }, /** * 字符串轉換爲json * @param str * @returns {any} */ string2Json: function (str) { return eval(str); } }); /*數組相關*/ xframe.extend(xframe, { /** * 將一個數組清空,並返回數組的引用 * 只須要把數組的元素置空爲0便可 * @return {xframe} */ clear: function () { this.length = 0; return this; }, /** * 返回數組的第0個元素 * @return {*} */ first: function () { return this[0]; }, /** * 返回數組的最後一個元素 * @return {*} */ last: function () { return this[this.length - 1]; }, /** * 計算一個數組的大小尺寸 * @return {number|*} */ size: function () { return this.length; }, cacl: function (arr, callback) { var ret; for (var i = 0; i < arr.length; i++) { // 專門用於處理每一項的計算機過程 ret = callback(arr[i], ret); } return ret; }, /** * 對數組裏面的全部元素求和 * @return {*} */ sum: function () { // 1. 正常寫法 var ret; for (var i = 0; i < this.length; i++) { ret = ret + this[i]; } return ret; }, max: function () { }, min: function () { }, avg: function () { }, intersect: function () { }, union: function () { }, diff: function () { }, unique: function () { }, forEach: function () { }, map: function () { }, filter: function () { }, every: function () { }, some: function () { }, reduce: function () { }, reduceRight: function () { }, indexOf: function () { }, lastIndexOf: function () { }, enhanceUnique: function () { }, without: function () { }, flatten: function () { }, random: function () { }, removeAt: function () { }, contains: function () { } }); /*Math*/ xframe.extend(xframe, { random: function () { } }); /*數據類型檢驗*/ xframe.extend(xframe, { // 鴨子類型(duck typing)若是它走起路來像鴨子,叫起來也是鴨子,那麼它就是鴨子。 // 只關注對象的行爲,不關注對象自己面向接口編型 ,而不是面向實現編程,是設計模式中最重要的思想。 // 【理解】:一個對象有效的語義,不是由集成自特定的類或實現特定的接口, 而是由當前方法和屬性的集合決定的!!! isNumber: function (val) { // 若是這個數字是有限的話, 並且是數字類型 return (typeof val === 'number' && isFinite(val)) && (Object.prototype.toString.call(val) === '[object Number]'); }, /*** * 判斷一個變量是否是Boolean類型 * @param val * @returns {boolean} */ isBoolean: function (val) { return (typeof val === 'boolean') && (Object.prototype.toString.call(val) === '[object Boolean]'); }, /** * 判斷一個變量是否是字符串類型 * @param val * @returns {boolean} */ isString: function (val) { return (typeof val === 'string') && (Object.prototype.toString.call(val) === '[object String]'); }, /** * 判斷一個變量是否是undefined * @param val * @returns {boolean} */ isUndefined: function (val) { // oid 0 is a correct and standard way to produce undefined. return (val === void 0) || (typeof val === 'undefined') && (Object.prototype.toString.call(val) === '[object Undefined]'); }, /** * 判斷一個變量是否是爲空 * @param val * @returns {boolean} */ isNull: function (val) { return (val === null) && (Object.prototype.toString.call(val) === '[object Null]'); }, /** * 檢測 * @param obj * @returns {*} */ isNaN: function (val) { // 只要這個數字經過判斷是否是和他自身相同或者使用typef的方式去檢測 return val !== val; }, /** * 判斷一個變量是否是一個對象類型 * @param val * @returns {boolean} */ isObject: function (val) { if (val !== null && val !== undefined) { if ((typeof val === 'object') && (Object.prototype.toString.call(val))) { return true; } } return false; }, /** * 判斷一個對象是否是數組對象 * @param val * @returns {boolean|void|string} */ isArray: function (val) { // 判斷上不是一個數組的先判斷這個數組對象是否是爲空, 由於若是val爲空的話,就是val.constructor這個屬性其實是沒有的,error if (val !== null || typeof val !== "undefined") { // 注意在使用constructor判斷數據類型的時候比較的其實是他的原型對象的constructor屬性, 這個屬性指向的其實是這個變量的原型對象 return (val.constructor === Array) && (Object.prototype.toString.call(val) === '[object Array]'); } return false; } }); /*數組化:arguments, document.forms, document.getElementsByName, document.getElementsByTagName()*/ xframe.extend(xframe, { /** * 把一個僞數組轉換爲一個新的數組 * 實現思路: 取出僞數組中的每個元素, 而後把取出來的這些元素從新放入到一個新的數組裏面去!!! * @param start * @param end * @returns {Array} */ toArray: function (start, end) { var result = []; var start = start || 0, // 這裏的this指向調用的對象,使用了call以後, 改變了this的指向, 指向傳進來的對象(外邊必需要修改this的指向) // 若是外邊不修改this的指向,這裏的this默認指向的是xframe這個框架對象 end = end || this.length; for (var i = start; i < end; i++) { result.push(this[i]); } return result; }, /** * 方法二: 直接把一個僞數組轉換爲JavaScript中的一個數組對象 * @param obj * @returns {T[]} */ slice: function (obj) { return Array.prototype.slice.apply(obj); } }); /*domReady的實現*/ xframe.extend(xframe, { //arguments 的主要用途是保存函數參數, 但這個對象還有一個名叫 callee 的屬性,該屬性是一個指針,指向擁有這個 arguments 對象的函數 /** * 實現一個domReady方法:全部元素都加載完畢以後一個回調函數 * @param domElement * @param fn */ onDOMReady: function (fn) { if (document.addEventListener) { // W3C組織: 若是傳過來的是一個DOM元素的話,就直接對這個DOM元素添加監聽, 不然,就對整個document添加事件監聽 document.addEventListener('DOMContentLoaded', fn, false); } else { // IE瀏覽器 IEContentLoaded(fn); } /** * 微軟的IE瀏覽器的處理方法 * @param fn * @constructor */ function IEContentLoaded(fn) { // 定義須要的全局變量 var done = false, document = window.document; // 這個函數只會在全部的DOM節點樹建立完畢的時候纔會繼續向下執行 var init = (function () { if (!done) { console.log('done……'); // 若是DOM樹建立完畢的話 done = true; fn(); } })(); /* 使用這個當即函數來調用IE瀏覽器的內置函數實現domReady的功能 */ (function () { try { // DOM樹在未建立完畢以後調用 doScroll的話,會拋出錯誤 document.documentElement.doScroll('left'); } catch (err) { // 延遲1秒以後再次執行這個函數, 造成一個函數遞歸調用的功能【回調函數】 // clllee是一個函數指針,指向的是擁有這個arguments對象的函數, 從而實現再次調用這個函數 setTimeout(arguments.callee, 1); return; } // 若是沒有錯誤的話,表示DOM樹已經徹底建立完畢, 此時開始執行用戶的回調函數 init(); })(); // 監聽document的加載狀態(DOM加載的過程當中會不斷回調這個函數) document.onreadystatechange = function () { console.log('onreadystatechange……'); if (document.readyState === 'complete') { console.log('complete……'); // 若是加載完成的話 document.onreadystatechange = null; init(); } } } } }); })(xframe); // 事件框架 ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法) xframe.extend({ /** * 實現一個瀏覽器的基本事件的綁定 * @param type * @param fn * @return {on} */ on: function (type, fn) { // 注意這裏的初始的下標編號是長度減一 var i = this.length - 1; // 能夠實現兼容版本的IE瀏覽器和W3c瀏覽器的支持 if (document.addEventListener) { // w3c(這裏使用的方式是從後向前遍歷, 使得每個DOM加載完畢以後再去添加事件) for (; i >= 0; i--) { this[i].addEventListener(type, fn, false); } } else if (document.attachEvent) { // IE for (; i >= 0; i--) { this[i].attachEvent('on' + type, fn); } } else { // 其餘的瀏覽器 for (; i >= 0; i--) { // 獲取json數據的兩種方式,綁定事件的方式也能夠 this[i]['on' + type] = fn; } } return this; }, /** * 實現事件的解除綁定 * @param type * @param fn * @return {un} */ un: function (type, fn) { // 注意這裏的初始下標編號 var i = this.length - 1; if (document.removeEventListener) { // W3c for (; i >= 0; i--) { this[i].removeEventListener(type, fn, false); } } else if (document.detachEvent) { // IE瀏覽器 for (; i >= 0; i--) { this[i].detachEvent(type, fn); } } else { // 其餘瀏覽器的話,就直接默認綁定的全部事件置爲null for (; i >= 0; i--) { // 移出全部綁定的事件 this[i]['on' + type] = null; } } return this; }, /** * 實現單個元素的事件綁定 * @param fn * @return {click} */ click: function (fn) { this.on('click', fn); return this; }, /** * 實現鼠標移動進來和出去的事件響應(鼠標懸浮事件) * @param fnOver * @param fnOut * @return {hover} */ hover: function (fnOver, fnOut) { var i = this.length; // 仍是採用的是從後向前遍歷的方式 for (; i >= 0; i--) { if (fnOver && typeof fnOver === 'function') { this.on('mouseover', fnOver); } if (fnOut && typeof fnOut === 'function') { this.on('mouseout', fnOut); } } return this; }, /** * 若是被選元素可見,則隱藏這些元素,若是被選元素隱藏,則顯示這些元素。 * toggle方法,切換,接收任意個參數,不斷在參數間循環.例:點擊顯示隱藏 * @return {toggle} */ toggle: function () { // 實現一個事件的切換f1, f2 var self = this, _arguments = arguments, i = 0, len = this.length; // 把全部的事件響應函數存起來 for (; i < len; i++) { addToToggle(this[i]); } /** * 鼠標點擊以後逐個調用本身綁定的事件 * @param obj */ function addToToggle(obj) { // 定義一個私有的計數器 var count = 0; // 添加事件 self.on('click', function () { // 使用call去修改this的指向(這裏的主要做用是去切換,輪巡切換狀態) _arguments[count++ % _arguments.length].call(obj); }); } return this; } }); // 不須要參與鏈式訪問的 xframe.extend(xframe, { /** * 獲取事件對象 * @param event * @return {Event} */ getEvent: function (event) { return event ? event : window.event; }, /** * 獲取觸發事件的元素 * @param event * @return {*|Element|Object} */ getTarget: function (event) { var event = this.getEvent(event); return event.target || event.srcElement; }, /** * 阻止事件冒泡 * @param event */ stopPropagation: function (event) { var event = this.getEvent(event); if (event.stopPropagation) { // W3c event.stopPropagation(); } else { // IE event.cancelBubble = true; } }, /** * 阻止默認的行爲 * @param event */ preventDefault: function (event) { var event = this.getEvent(event); if (event.preventDefault) { // w3c event.preventDefault(); } else { // IE event.returnValue = false; } }, /** * 獲取鼠標滾輪的運動的詳細信息 * @param event * @return {*} */ getDelta: function (event) { var event = this.getEvent(event); if (event.wheelDelta) { // w3c return event.wheelDelta; } else { // ie // Firefox的值有所不一樣,所以首先要將這個值的符號反向,而後再乘以40,就能夠保證與其它瀏覽器的值相同了 return -event.detail * 40; } } }); })(xframe); // CSS 樣式框架 ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法)【只要是須要使用到this獲取到的元素集合這個變量的時候,這裏就是須要進行鏈式訪問的】 xframe.extend({ /** * 給DOM元素設置/取值CSS樣式 * @return {*} */ css: function () { // 分爲兩種狀況,一種是取值模式,一種是設置模式 var arg = arguments, len = arg.length, j = this.length - 1; if (len === 0) { // 沒有參數的話,就直接返回這個DOM集合 return this; } else if (len === 1) { // 取值模式 if (typeof arg[0] === 'string') { if (this[0].currentStyle) { // w3c return this[0].currentStyle[arg[0]]; } else { // 其餘IE return getComputedStyle(this[0], false)[arg[0]]; } } else if (typeof arg[0] === 'object') { // 若是要獲取一系列對象的屬性信息, 若是傳過來的一個參數是一個json對象的話,這裏也採用這種方式 // {name : xiugang, age : 18} for (var item in arg[0]) { // 從後向前開始遍歷,設置模式 for (; j >= 0; j--) { // 因爲CSS在設置值的時候的取值模式和設置模式的不一樣,這裏須要先使用駝峯表示法進行處理一下 // 先把item轉換爲:backgroundcolor --> backgroundColor item = $.camelCase(item) this[j].style[item] = arg[0][item]; } } } } else if (len === 2) { // 設置模式 for (; j >= 0; j--) { // 第一個參數是咱們須要設置的值 this[j].style[$.camelCase(arg[0])] = arg[1]; } } return this; }, /** * 隱藏一個元素 * @return {hide} */ hide: function () { var j = this.length - 1; for (; j >= 0; j--) { this[j].style.display = 'none'; } return this; // 方法二:使用以前封裝好的框架進行遍歷 this.each(function () { this.style.display = 'none'; }) }, /** * 顯示元素 * @return {show} */ show: function () { this.each(function () { this.style.display = 'block'; }) return this; }, /** * 獲取元素的寬度 * @return {*} */ width: function () { return this[0].clientWidth; }, /** * 獲取元素的高度 * @return {*} */ height: function () { return this[0].clientHeight; }, /** * //當元素出現滾動條時候,這裏的高度有兩種:可視區域的高度 實際高度(可視高度+不可見的高度) * 獲取元素的滾動寬度 * @return {*} */ scrollWidth: function () { return this[0].scrollWidth; }, /** * 獲取元素的滾動高度 * @return {*} */ scrollHeight: function () { return this[0].scrollHeight; }, /** * 元素滾動的時候 若是出現滾動條 相對於左上角的偏移量 * @return {*} */ scrollTop: function () { return this[0].scrollTop; }, /** * 元素滾動的時候相對於左上角的距離 * @return {*} */ scrollLeft: function () { return this[0].scrollLeft; }, }); // 不須要參與鏈式訪問的 xframe.extend(xframe, { getThis: function () { console.log(xframe, typeof this); // function, 這裏的this指向的其實是一個函數function (selector, context) }, /** * 獲取屏幕的高度 * @return {number} */ screenHeight: function () { return window.screen.height; }, /** * 虎丘屏幕的款U盾 * @return {number} */ screenWidth: function () { return window.screen.width; }, /** * 獲取瀏覽器窗口文檔顯示區域的寬度,不包括滾動條 * @return {number} */ wWidth: function () { return document.documentElement.clientWidth; }, /** * 獲取瀏覽器窗口文檔顯示區域的高度,不包括滾動條 * @return {number} */ wHeight: function () { return document.documentElement.clientHeight; }, /** * 文檔滾動區域的總體的高 * @return {number} */ wScrollHeight: function () { return document.body.scrollHeight; }, /** * 文檔滾動區域的總體的寬度 * @return {number} */ wScrollWidth: function () { return document.body.scrollWidth; }, /** * 獲取滾動條相對於其頂部的偏移 * @return {number} */ wScrollTop: function () { var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; return scrollTop; }, /** * 獲取整個文檔窗口的距離整個窗口的寬度和高度(滾動條相對於頂部和左邊的距離) * @return {number} */ wScrollLeft: function () { var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; return scrollLeft; } }); })(xframe); // 選擇框架 ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法) xframe.extend({}); // 不須要參與鏈式訪問的 xframe.extend(xframe, { /** * ID選擇器 * @param context * @return {HTMLElement | *} */ $id: function (context) { // context是一個DOM對象仍是字符串 context = this.isString(context) ? document.getElementById(context) : context; return context; }, /** * tag選擇器, context;裏面存儲了上下文信息(儘可能少的使用局部變量) * @param tag * @param context * @return {NodeListOf<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>} */ $tag: function (tag, context) { // 分爲兩種狀況 if (typeof context === 'string') { context = this.$id(context); } // 按照這種思路,只有多是一種狀況 if (context) { if (context.length) { // 這裏默認只會返回數組中的第0個元素 return [].slice.call(context)[0].getElementsByTagName(tag); } else { return context.getElementsByTagName(tag); } } return document.getElementsByTagName(tag); }, /** * 實現一個類選擇器 * @param className * @param context * @return {*} */ $class: function (className, context) { // context裏面此時存儲的是一個DOM節點元素 // 若是直接傳過來的是一個DOM元素節點context(DOM元素的話就單獨處理) context = this.$id(context) || document; // 1.因爲getElementByClassName()這個方法是不兼容的,所以須要使用瀏覽器內置的方法去獲取類選擇器 // 2. 可使用getElementByTagName()的方法去獲取全部的標籤元素,而後把再使用className的屬性間接去實現一個相似的class選擇器的功能 if (context.getElementsByClassName) { // 若是支持這個方法的話 return context.getElementsByClassName(className); } else { // 不支持的話就間接獲取 var doms = context.getElementsByTagName('*'), res = []; // 使用本身定義的方法去實現一個類選擇器 doms.each(function () { if (this.className === className) { // 只要是找到了這個class的集合,就放入到一個數組裏面 res.push(this); } }); return res; } }, /** * 使用管道思想實現一個層次選擇器 * @return {Array} */ $cengci: function () { var self = this; // 主要功能:實現一個層次選擇器 // 輸入字符串: str = '#className div a p' 選擇全部的className 下面的P標籤 // 1. 獲取穿過來的參數(數組元素去重) var args = Array.prototype.slice.call(arguments)[0].toString().split(' '), index, first, item, selector, res = [], // 存儲了本次的結果信息 context = []; // 存儲了上一次的上下文信息【管道思想!】, context = 'tag .class #id' // 思考: 爲了實現一個層次選擇器, 如何實現一個吧上一次選擇的元素所有存儲起來??? // 2. 開始解析參數信息 args.each(function () { // 每次重複以前,先把本次須要存儲的數組清空(res裏面存儲了每次的最新數據) res = []; // 對獲取到的每一項進行處理 item = this.trim(); first = item.charAt(0); index = item.indexOf(first); selector = item.slice(index + 1); // 使用管道思想實現一個層次選擇器!!! switch (first) { case '.': // class 選擇器 if (context.length) { // 說明這一次的class類選擇器中的元素不是第一次出現 context.each(function () { pushArray(self.$class(selector, this)); }); } else { // 若是是第一次出現的話 pushArray(self.$class(selector)); } // 把上一次執行的結果存起來 context = res; break; case '#': // ID選擇器 // 因爲ID選擇器獲取的元素始終是惟一的,所以直接放進去便可 res.push(self.$id(selector)); // 把上一次執行的結果存起來 context = res; break; default: // tag選擇器 if (context.length) { // 說明不是第一次出現 context.each(function () { // 注意在使用tag選擇器的時候,第二個參數必須是一個ID選擇器,或者是一個 // 1. 注意在放入數組的時候,須要逐個遍歷而後放進去 pushArray(self.$tag(item, this)); }); } else { // 第一次出現的 pushArray(self.$tag(item)); } // 把上一次執行的結果存起來 context = res; break; } }); /** * 把公共的部分代碼封裝起來 * @param doms */ function pushArray(doms) { if (doms) { [].slice.call(doms).each(function () { res.push(this); }); } } return context; }, /** * group選擇器 * @return {Array} */ $group: function () { var self = this; // '.moshou,#moshou,span,.dream' // 1. 獲取傳過來的參數 var args = [].slice.call(arguments), arr = args[0].split(',').unique(), // 這裏在拿到這個分割後的字符串後,開始進行數組元素去重 item, index, first, selector; res = []; // 2. 開始遍歷參數集合,解析參數信息 arr.each(function () { // 3. 開始遍歷獲得結果,獲取每一項 item = this.trim(); // 4. 開始獲取首字母信息,和後面的選擇器信息 // 4. 獲取指定下標位置對應的字符 first = item.charAt(0); index = item.indexOf(first); selector = item.slice(index + 1); // 開始根據第一個字母向下進行判斷,把知足相應條件的放在數組裏面 switch (first) { case '.': // class選擇器 res.push(self.$class(selector)); break; case '#': // ID 選擇器 res.push(self.$id(selector)); break; default: // TAG選擇器(直接就是first自己,這裏不用再判斷了使用selector這個變量了) res.push(self.$tag(item)); break; } }); return res; }, /** * 多組+層次選擇器 * @return {Array} */ $select: function () { // str = '#tag , .calss' var args = [].slice.call(arguments)[0].toString().split(','), ret = [], self = this; // 遍歷args數組,對數組的每一項採用層次選擇器 args.each(function () { // 1. 對於逗號分隔的部分採用層次選擇,獲取層次選擇器的結果信息, 是一個數組集合 var res = self.$cengci(this); // 2. 遍歷層次選擇器的集合,把信息放入到一個新的數組裏面, 就是獲得的多組選擇器的結果信息 pushArray(res); }); // 層次選擇器 function pushArray(doms) { if (doms.length) { doms.each(function () { ret.push(this); }); } } return ret; } }); })(xframe); // 屬性框架 ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法) xframe.extend({ /** * 獲取/設置某一個元素的屬性信息 * @return {*} */ attr: function () { // 獲取屬性信息:兩種格式,1. 取值模式 2.設置模式 var args = arguments; if (args.length === 0) { // 沒有參數的話,就直接返回自己 return this; } else if (args.length === 1) { // 一個參數的話須要進行判斷 if (typeof args[0] === 'string') { // 取值模式 return this[0].getAttribute(args[0]); } else if (typeof args[0] === 'object') { // json對象的話也算是一個設置模式 for (var item in args[0]) { Array.prototype.slice.call(this).each(function () { this.setAttribute(item, args[0][item]); }); } } } else if (args.length === 2) { Array.prototype.slice.call(this).each(function () { this.setAttribute(args[0], args[1]); }); } // 注意這裏的this實際上返回的是一個xframe實例對象,可是xframe.eatend(xframe, {})這裏的this其實是一個xframe(selector, context)函數, 尚未實例化呢 return this; }, /** * 判斷DOM元素節點是否是擁有某一個屬性 * @param val * @return {boolean} */ hasClass: function (val) { if (!this[0]) { return false; } // 默認只會獲取第一個元素的相關信息 return this[0].className === val.trim() ? true : false; }, /** * 添加一個class class='xiugang 18 nan' * @param val */ addClass: function (val) { // 處理傳進來的字符串兩邊的空格 val = val.trim(); [].slice.call(this).each(function () { // 只要原來的DOM節點上面沒有這個屬性的話,就直接添加上去 if (val !== this.className) { this.className += ' ' + val; } }) return this; }, /** * 注意熟練掌握replace()函數的使用 * @param val */ removeClass: function (val) { val = val.trim(); [].slice.call(this).each(function () { if (val === this.className) { // 使用後面替換前面的 this.className = this.className.replace(val, ''); } }) return this; }, /** * 若是有的話就直接刪除,沒有的話就添加一個 * @param val * @return {toggleClass} */ toggleClass: function (val) { val = val.trim(); [].slice.call(this).each(function () { if (val === this.className) { // 若是有的話就直接刪除 this.className.replace(val, ''); } else { // 沒有的話就添加一個 this.className += ' ' + val; } }); return this; } }); // 不須要參與鏈式訪問的 xframe.extend(xframe, {}); })(xframe); // 內容框架 ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法) xframe.extend({ /** * .html()用爲讀取和修改元素的HTML標籤 對應js中的innerHTML * @return {html} */ html: function () { var arg = arguments, len = arg.length, arr = Array.prototype.slice.call(this); if (this.length < 1) { return this; } // 分爲取值模式和設置模式 if (len === 0) { // 取值模式 return this[0].innerHTML; } else if (len === 1) { // 設置模式 arr.each(function () { this.innerHTML = arg[0]; }); } return this; }, /** * 用於獲取文本信息 * @return {*} */ text: function () { var args = arguments, len = args.length; if (this.length === 0) { return this; } if (len === 0) { // 取值模式 return this[0].innerText; } else if (len === 1) { // 設置模式 this.each(function () { this.innerText = args[0]; }); } return this; }, /** * 用於獲取表單中的數值(input, form) * @return {*} */ val: function () { // val();設置或者獲取表單字段的值(前提是表單設置了value屬性); var args = arguments, len = args.length; if (this.length === 0) { return this; } if (len === 0) { return this[0].value; } else if (len === 1) { this.each(function () { this.value = args[0]; }); } return this; } }); // 不須要參與鏈式訪問的 xframe.extend(xframe, {}); })(xframe); // DOM框架(選擇器框架) ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法) xframe.extend({ /** * 向現有的元素集合中添加元素節點(修改this的內容) * @param dom * @return {add} */ add: function (dom) { // 1. 項僞數組中添加元素 this[this.length] = dom; // 2. 數組的長度也須要改變了 this.length++; return this; }, /** * 向現有的元素節點中添加dom節點(對使用選擇器獲取的一系列元素都添加孩子節點child) * @param child,這裏建立的其實是一個JQuery對象 */ append: function (child) { // 這裏獲取的實際上就是隻有一個的 var doms = typeof child === 'string' ? $(child) : $(child[0]), arr = Array.prototype.slice.call(doms); //console.log(typeof doms[0], typeof arr[0]); // 2. 調用本身的方法將一個僞數組轉換爲數組,並開始遍歷 /*for (var i = 0; i < this.length; i++){ for (var j = 0; j < doms.length; j++){ // 注意這裏的操做, 因爲在每次添加一個新的元素以後, this的長度就會增長,所以這裏在修改以前先把this.length修改一下 this[i].appendChild(doms[j]); } }*/ /*this.each(function (element) { arr.forEach(function (childNode) { element.appendChild(childNode); }); });*/ // 這裏的處理目的是,若是穿過來的DOM節點只是有一個的話須要建立和this長度相同的DOM元素 if (arr.length !== this.length) { arr = []; // 至關因而把自己複製幾份 Array.prototype.slice.call(this).each(function () { arr.push(doms[0]); }); } // 開始向父親節點添加元素 Array.prototype.slice.call(this).forEach(function (element, index) { element.appendChild(arr[index]); }); // 開始向我獲取的this節點裏面添加數據 /*for (var i = 0; i < this.length; i++){ for (var j = 0; j < arr.length; j++){ if (this[i].childNodes){ continue; } // 注意這裏的操做, 因爲在每次添加一個新的元素以後, this的長度就會增長,所以這裏在修改以前先把this.length修改一下 this[i].appendChild(arr[j]); } }*/ }, /** * 把選擇器中的節點添加到父容器中 * @param parent */ appendTo: function (parent) { // 1. 獲取全部的父容器 var doms = $(parent), self = this; // 2. 向父容器中添加孩子節點 Array.prototype.slice.call(this).forEach(function (element, index) { doms[index].appendChild(self[index]); }); return this; }, /** * 獲取指定下表下面的DOM節點 * @param num * @return {null} */ get: function (num) { return this[num] ? this[num] : null; }, /** * 獲取一個相似於JQuery的對象實例 * @param num * @return {jQuery|HTMLElement} */ eq: function (num) { // 1. 獲取一個JQuery對象,首先先獲取這個DOM元素節點 var dom = this.get(num); // 2. 把這個DOM節點轉換爲一個JQuery對象 return $(dom); }, /** * 獲取第一個JQuery對象 * @return {*|jQuery|HTMLElement} */ first: function () { return this.eq(0); }, /** * 獲取最後一個JQuery對象 * @return {*|jQuery|HTMLElement} */ last: function () { return this.eq(this.length - 1); }, /** * 獲取一個DOM節點的全部子節點 * @return {array} */ children: function () { // 獲取一個元素的全部的孩子節點 // 1. 定義一個僞數組, 用於存儲全部的孩子節點, 而後獲取默認的第一個元素的全部孩子節點 var children = this[0].children, len = children.length, that = {}, i = 0; // 初始化定義的這個僞數組 that.length = len; for (; i < len; i++) { that[i] = children[i]; } return that; }, /** * 從當前DOM元素節點向下尋找一層元素節點 * @param str * @return {} */ find: function (str) { var res = [], self = this, doms; this.each(function () { switch (str.charAt(0)) { case '.': // 類選擇器 doms = $.$class(str.substring(1), self[i]); pushArray(doms); break; default: // 標點選擇器 doms = $.$tag(str, self[i]); pushArray(doms); break; } }); function pushArray(doms) { if (doms.length) { self.toArray(doms, function () { res.push(this); }); } } // 【注意:】爲了可以返回一個JQuery對象,這裏須要再次進行處理 var that = this; that.length = this.length; this.each(function (index) { // 這裏須要再次構造一個僞數組對象,從而實現鏈式訪問的功能 that[index] = res[index]; }); // 這裏在修改that的時候實際上會間接地把this這個變量修改了 return that; }, /** * 獲取一個元素的父類節點 * @return {parent} */ parent: function () { // 獲取父節點,而且返回一個JQuery對象 var parent = this[0].parentNode; this[0] = parent; this.length = 1; // 因爲每個元素只會有一個父類節點,所以長度爲1 return this; }, /** * 獲取一個元素在同一個級別的元素裏面的下表編號 * @return {number} */ index: function () { // 獲取元素自己在同一個級別下面的元素下表編號 var srcNode = this[0], children = srcNode.parentNode.children, self = this, defaultRes = -1; self.toArray(children, function (index) { // 這裏的this指向的就是每個元素, index指向的就是元素的下表編號 if (children[index] === srcNode) { defaultRes = index; } }); // 返回查詢到的結果下標 return defaultRes; } }); // 不須要參與鏈式訪問的 xframe.extend(xframe, { /** * 建立一個DOM元素節點 * @param type * @param value * @param html * @return {*} */ create: function (type, value, html) { var dom = document.createElement(type); return xframe().add(dom).attr(value).html(html); }, /** * 直接的孩子節點 * @param dom * @param tag * @return {jQuery|HTMLElement} */ directChildren: function (dom, tag) { var res = [], tag = tag; if (typeof dom === 'string') { dom = $(dom); } // 若是是一個元素集合的處理方法 if (dom.length) { Array.prototype.slice.call(dom).each(function () { getDOM(this.children); }); } else { // 若是隻是一個元素的處理方法 getDOM(dom.children); } /** * 主要用於把知足已知條件的DOM元素集合統一放入到一個新的res數組裏面去 * @param doms */ function getDOM(doms) { Array.prototype.slice.call(doms).each(function () { if (this.tagName.toLowerCase() === tag.toLowerCase()) { res.push(this); } }); } // 若是得到了這個直接子節點,就直接返回這個對象 return $(res); }, }); })(xframe); // 動畫框架 ;(function (xframe) { // 須要參與鏈式訪問的(必須使用prototype的方式來給對象擴充方法) xframe.extend({}); // 不須要參與鏈式訪問的 xframe.extend(xframe, {}); // 實現動畫框架的封裝 xframe.Animate = (function (xframe) { // 1. 定義須要的API接口(API內部用於放置屬性) var api = { timer: null,// 這是一個動畫循環句柄 queen: [] // 多個對象同時運行的一個數組隊列 }; // 運行部門------------------------------------------------- /** * 在把須要的運行參數都準備好了以後(多個對象),就開始執行這個運行函數 */ api.run = function () { // 定義一個定時器,用於不斷地執行我本身定義的動畫函數信息 api.timer = setInterval(function () { // 因爲全部的參數都已經準備好了,所以這裏只須要直接進行循環操做便可 api.loop(); }, 16); // 這裏循環的週期設置的是16mm } /** * 執行動畫循環操做 */ api.loop = function () { // obj裏面存儲了obj = {id, now, pass, tween, duration, style} api.queen.forEach(function (obj) { // 遍歷隊列中的每一項參數,開始執行移動操做 api.move(obj); }); } /** * 實現物體的移動 */ api.move = function (obj) { // 1. 計算當前的時間 obj.pass = +new Date(); // 2. 獲取動畫時間進程(這裏的動畫樣式默認是一個彈簧的顯示樣式) var tween = api.getTween(obj.now, obj.pass, obj.duration, 'easeOutBounce'); // 注意咱們再每一次移動這個物體對象以前須要把這個物體對象的動畫時間進程更新一下,這樣到了後面的修改對象的屬性的時候這個參數的數值纔會動態改變 obj.tween = tween; //console.log(tween); // 3. 設置屬性信息 if (tween >= 1) { // 若是動畫時間進程結束了(百分比信息) api.stop(); } else { // 4. 經過設置對象的屬性信息來移動每個對象 api.setManyProperty(obj); } } // 添加部門------------------------------------------------- /** * @param 獲取用戶輸入的參數,開始對參數進行解析,開始添加參數,而後實現動畫的開始運行 */ api.add = function () { var args = arguments, id = args[0], json = args[1], duration = args[2]; // 獲取輸入的參數,而後開始使用適配器解析數據 try { // 1. 調用適配器準備參數 api.adapterMany(id, json, duration); // 2. 開始運行動畫 api.run(); } catch (e) { console.error(e.message); } } /** * 這是一個適配器,用於解析一個對象的參數信息(只能處理一個對象) * @param id * @param json * @param duration */ api.adapterOne = function (id, json, duration) { var obj = {} // 這裏的OBj就是一個字面量格式, 用於存儲須要的參數信息 obj.id = id // ID編號 obj.now = +new Date() // 開始時間 obj.pass = 0 // 當前時間 obj.tween = 0 // 動畫時間進程 obj.duration = duration // 動畫的持續時間 obj.styles = [] // 用於存放全部的樣式信息 // 根據用戶輸入的參數信息選擇不一樣的動畫速度 if ($.isString(duration)) { switch (duration) { case 'slow': case '慢': duration = 8000; break; case 'normal': case '普通': duration = 4000; break; case 'fast': case '快': duration = 1000; break; } } // 設置樣式信息 obj.styles = api.getStyles(id, json); return obj; } /** * 這個適配器針對的是處理多個對象的動畫信息 * @param id * @param json * @param data */ api.adapterMany = function (id, json, data) { // 處理多個對象的參數信息(一樣的參數,可是須要處理不一樣的信息,針對的是多個對象的參數) var obj = this.adapterOne(id, json, data); // 開始向我已有的隊列中添加數據信息(此時queen隊列裏面就是存放了我全部的數據信息) api.queen.push(obj); } /** * 獲取樣式信息 * @param id * @param json */ api.getStyles = function (id, json) { // animate('#sun', {left: 200, top : 500}, 7000); // 把用戶傳遞過來的參數信息轉換我須要的格式 var styles = []; // 開始解析json數據信息 for (var item in json) { var style = {}; // 這裏的item就是下面的:left, top style.name = item; // 獲取物體開始的位置 style.start = parseFloat($(id).css(item).toString()); // 計算物體的偏移量(移動的距離) style.length = parseFloat(json[item]) - style.start; styles.push(style); } return styles; } /** * 用於獲取一個動畫時間進程 * @param now 開始時間 * @param pass 當前時間 * @param all 持續時間 * @param ease 動畫效果 */ api.getTween = function (now, pass, all, ease) { // 1.定義常見的動畫效果 var eases = { //線性勻速 linear: function (t, b, c, d) { return (c - b) * (t / d); }, //彈性運動 easeOutBounce: function (t, b, c, d) { if ((t /= d) < (1 / 2.75)) { return c * (7.5625 * t * t) + b; } else if (t < (2 / 2.75)) { return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b; } else if (t < (2.5 / 2.75)) { return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b; } else { return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b; } }, //其餘 swing: function (t, b, c, d) { return this.easeOutQuad(t, b, c, d); }, easeInQuad: function (t, b, c, d) { return c * (t /= d) * t + b; }, easeOutQuad: function (t, b, c, d) { return -c * (t /= d) * (t - 2) + b; }, easeInOutQuad: function (t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t + b; return -c / 2 * ((--t) * (t - 2) - 1) + b; }, easeInCubic: function (t, b, c, d) { return c * (t /= d) * t * t + b; }, easeOutCubic: function (t, b, c, d) { return c * ((t = t / d - 1) * t * t + 1) + b; }, easeInOutCubic: function (t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; return c / 2 * ((t -= 2) * t * t + 2) + b; }, easeInQuart: function (t, b, c, d) { return c * (t /= d) * t * t * t + b; }, easeOutQuart: function (t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, easeInOutQuart: function (t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; return -c / 2 * ((t -= 2) * t * t * t - 2) + b; }, easeInQuint: function (t, b, c, d) { return c * (t /= d) * t * t * t * t + b; }, easeOutQuint: function (t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b; }, easeInOutQuint: function (t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; }, easeInSine: function (t, b, c, d) { return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; }, easeOutSine: function (t, b, c, d) { return c * Math.sin(t / d * (Math.PI / 2)) + b; }, easeInOutSine: function (t, b, c, d) { return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; }, easeInExpo: function (t, b, c, d) { return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; }, easeOutExpo: function (t, b, c, d) { return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; }, easeInOutExpo: function (t, b, c, d) { if (t == 0) return b; if (t == d) return b + c; if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; }, easeInCirc: function (t, b, c, d) { return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; }, easeOutCirc: function (t, b, c, d) { return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; }, easeInOutCirc: function (t, b, c, d) { if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; }, easeInElastic: function (t, b, c, d) { var s = 1.70158; var p = 0; var a = c; if (t == 0) return b; if ((t /= d) == 1) return b + c; if (!p) p = d * .3; if (a < Math.abs(c)) { a = c; var s = p / 4; } else var s = p / (2 * Math.PI) * Math.asin(c / a); return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; }, easeOutElastic: function (t, b, c, d) { var s = 1.70158; var p = 0; var a = c; if (t == 0) return b; if ((t /= d) == 1) return b + c; if (!p) p = d * .3; if (a < Math.abs(c)) { a = c; var s = p / 4; } else var s = p / (2 * Math.PI) * Math.asin(c / a); return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b; }, easeInOutElastic: function (t, b, c, d) { var s = 1.70158; var p = 0; var a = c; if (t == 0) return b; if ((t /= d / 2) == 2) return b + c; if (!p) p = d * (.3 * 1.5); if (a < Math.abs(c)) { a = c; var s = p / 4; } else var s = p / (2 * Math.PI) * Math.asin(c / a); if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b; }, easeInBack: function (t, b, c, d, s) { if (s == undefined) s = 1.70158; return c * (t /= d) * t * ((s + 1) * t - s) + b; }, easeOutBack: function (t, b, c, d, s) { if (s == undefined) s = 1.70158; return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; }, easeInOutBack: function (t, b, c, d, s) { if (s == undefined) s = 1.70158; if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; }, easeInBounce: function (t, b, c, d) { return c - this.easeOutBounce(d - t, 0, c, d) + b; }, easeInOutBounce: function (t, b, c, d) { if (t < d / 2) return this.easeInBounce(t * 2, 0, c, d) * .5 + b; return this.easeOutBounce(t * 2 - d, 0, c, d) * .5 + c * .5 + b; } }; // 2. 計算每一次動畫循環的小號時長 var yongshi = pass - now; // 3. 獲取相應的動畫效果 return eases[ease](yongshi, 0, 1, all); } /** * 經過設置一個對象的屬性信息來實現物體的運動效果(若是隻有一個屬性信息的話) * @param obj */ api.setOneProperty = function (obj) { // 用於設置一個對象的屬性信息(obj.id, obj.json) // 【注意點】:這裏是動畫實現的一個核心要點,經過修改對象的屬性信息來移動物體 if (obj.name === 'opacity') { $(obj.id).css(obj.name, (obj.start + obj.length * obj.tween)); } else { // 對於設置對象的其餘屬性信息都是須要添加一個px,像素值信息 $(obj.id).css(obj.name, (obj.start + obj.length * obj.tween) + 'px'); } } /** * 用於設置一個對象的鎖哥屬性信息 obj.json = {width : '200px', height : '500px', 'opacity' : '0.1'} */ api.setManyProperty = function (obj) { // 因爲obj.styles裏面是一個數組 obj.styles.forEach(function (style) { // 遍歷當前對象的全部樣式屬性信息 obj.name = style.name; obj.start = style.start; obj.length = style.length; api.setOneProperty(obj); console.log(obj.tween); }); // 因爲styles裏面只存儲了style.name, style.start, style.length三個屬性信息, 所以須要處理一下 } /** * 結束動畫的執行 */ api.stop = function () { clearInterval(api.timer); } // 後勤部門---------------------------------------------------- api.destory = function () { } // 用戶只須要把須要的參數添加進來們就能夠執行一個動畫 // 用戶只須要傳進來三個參數,id, json, duration就能夠實現一個動畫 xframe.animate = api.add; })(xframe); })(xframe); // 緩存框架 ;(function (xframe) { /** * 實現了緩存框架的臨時存儲功能(內存存儲) * @type {{data: Array, get: (function(*): *), add: xframe.cache.add, delete: (function(*): boolean), update: (function(*, *): boolean), isExist: (function(*): boolean)}} */ xframe.cache = { data: [], // 用於存儲本地的數據信息 /** * 用於獲取本地存儲的json數據信息 * @param key * @return {*} */ get: function (key) { var value = null; this.data.each(function () { if (key.trim() === this.key.trim()) { value = this.value; } }); return value; }, /** * 向本地存儲添加數據信息 * @param key * @param value */ add: function (key, value) { this.data.push({ key: key.trim(), value: value.trim() }); }, /** * 刪除指定的key的數據信息 * @param key * @return {boolean} */ delete: function (key) { // 刪除指定的key對應的數據信息 var status = false, // 定義一個狀態碼,用於標記刪除是否成功的狀態信息 self = this; this.data.forEach(function (element, index) { // 遍歷本地的數據存儲信息,進行比對數據信息 if (key.trim() === element.key.trim()) { // 指定開始的位置,開始刪除數組中的數據信息 self.data.splice(index, 1); status = true; } }); return status; }, /** * 修改指定的元素的數據信息 * @param key * @param value */ update: function (key, value) { var status = false; this.data.forEach(function (element) { if (key.trim() === element.key) { // key不變,只修改數值信息, 注意element是一個json對象,這個對象裏面包含了兩個屬性element.key和element.value這兩個 element.value = value.trim(); status = true; } }); return status; }, /** * 檢測一個指定的數據是否存在 * @param key * @return {boolean} */ isExist: function (key) { // 用於檢測某一個數據信息是否存在 this.data.forEach(function () { if (key.trim() === this.key) { return true; } }); return false; } } /** * 實現了一個Cookie框架的封裝(注意在把HTML轉換爲實體存儲的時候這裏默認是去掉了最末尾的分號) * @type {{getCookie: xframe.cookie.getCookie, setCookie: xframe.cookie.setCookie, deleteCookie: xframe.cookie.deleteCookie, clearAllCookies: xframe.cookie.clearAllCookies}} */ xframe.cookie = { /** * 根據cookie的名字獲取Cookie的詳細信息 * @param name * @return {*} */ getCookie: function (name) { // 去除轉義字符 var name = name.escapeHTML(), // 讀取文檔中的全部cookie屬性 allCookies = document.cookie; // 下面是一些Cookie的數據格式信息(默認返回的是一個字符串) // H_PS_645EC=af88R0s3e76Ig1PlwkvrhnGGtg4qt5pcZNPKBUntPI2vGearAlyZyjXjmKYn%2BkggUXbNjhg; // 1. 查找名稱爲name的cookie信息script3&5; //name = name.substring(0, name.length-1); // 當前步驟是爲了去除掉末尾的分號(轉換爲標準形式); name += '='; // 等號右邊的就是獲取的數值,左邊就是cookie的名稱信息 // 2. 獲取'name='這個字符串在整個Cookie信息字符串中出現的位置下標 var pos = allCookies.indexOf(name); // 3. 判斷是否存在這個cookie的信息 if (pos !== -1) { // 若是存在的話,就繼續處理 // 3. 計算'cookie='等號後面的位置 var start = pos + name.length; // 3. 從'cookie='的位置開始向後搜索, 一直到;的位置結束, 從start的位置向後搜索信息 var end = allCookies.indexOf(';', start); if (end === -1) { // 若是爲-1的話, 說明cookie信息列表裏面只有一個Cookie信息 end = allCookies.length; } // 4. 提取Cookie的數值信息 var value = allCookies.substring(start, end); // 5.處理以後反轉義後返回(反轉義的目的是將內容進行加密處理,防止攻擊)【測試狀態OK,因爲以前的內部存儲,必須先刪除全部的,在執行就ok了】 return value.unescapeHTML(); } else { // 沒有找到, 說明不存在這個cookie信息 return ''; } // 默認狀況下返回一個空的字符串 return ''; }, /** * 根據傳入的參數信息設置瀏覽器的cookie * @param name * @param value * @param days * @param path */ setCookie: function (name, value, days, path) { var name = name.escapeHTML(), value = value.escapeHTML(), expires = new Date(), _expires, res; //name = name.substring(0, name.length-1); // 當前步驟是爲了去除掉末尾的分號(轉換爲標準形式); // 設置cookie的過時時間(單位是毫秒) expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000); if (path === '') { path = ''; } else { path = (';path=' + path); } if (typeof expires === 'string') { _expires = ''; } else { // 使用UTC標準時間 _expires = (';expires=' + expires.toUTCString()); } // 設置cookie信息,【注意要點:】(設置COokie的時候,只要遇到分號就會當即結束,只會保存分號以前的內容) res = name + '=' + value + _expires + path; // document.cookie="userId=828; userName=hulk"; document.cookie = res; }, /** * 根據名稱信息和路徑信息刪除cookie * @param name * @param path */ deleteCookie: function (name, path) { var name = name.escapeHTML(), expires = new Date(); if (path === '') { path = ''; } else { path = (';path=' + path); } // 刪除以後從新設置cookie document.cookie = name + '=' + ';expires=' + expires.toUTCString() + path; }, /** * 清空全部的cookie信息 */ clearAllCookies: function () { // 1. 獲取瀏覽器中存儲的全部cookie信息 // "name&=xiuxiu& name=xiuxiu; script=<script>alert(2); script2=<script>alert(2); script3=<script>alert(2); script3&=<script>alert(2); script4&=<script>alert(2); a&=<a>alert(2)</a>&" var cookies = document.cookie.split(';'); if (cookies.length) { cookies.forEach(function (element) { // 拿到字符串:name&=xiuxiu& var index = element.indexOf('='), name = element.substring(0, index); // 實現思路:要想刪除某一個COOkie信息,只須要將cookie的name對應的值設置爲空便可 document.cookie = name + '=' + ';expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; }); } } } // 本地存儲框架localstorage的本地存儲 xframe.store = (function (xframe) { // 定義一個API,用於定義實現的本地存儲的API接口 var api = {}, localStorageName = 'localStorage', globalStorageName = 'globalStorage', win = window, doc = window.document, storage; // 首先先定義要實現的功能接口 api.set = function (key, value) { } api.get = function (key) { } api.remove = function (key) { } api.clear = function () { } /* * a) sessionStorage和localStorage都是window的屬性,也是Storage對象的實例,即:window.sessionStorage instanceof Storage返回True,window.localStorage instanceof Storage 返回True,也所以二者享有Storage的屬性和方法。 b) sessoinStorage存儲的數據在頁面會話結束時會被清空,頁面會話在瀏覽器窗口關閉前持續存在,包含頁面刷新和恢復。若新開標籤或窗口將新建一個會話,再次獲取sessionStorage將只限於當前會話,與先前會話的無關。localStorage存儲的數據不會 c) window.globalStorage自Gecko 13 (Firefox 13)起再也不支持。 * * */ if (localStorageName in win && win[localStorageName]) { // 拿到本地存儲的這個數據項 storage = win[localStorageName]; // 實現我本身定義的接口 /** * 設置本地存儲的內容 * @param key * @param value */ api.set = function (key, value) { storage.setItem(key, value); } /** * 獲取本地存儲的內容 * @param key * @return {*} */ api.get = function (key) { return storage.getItem(key); } /** * 移出其中的某一項 * @param key */ api.remove = function (key) { storage.removeItem(key); } /** * 清空本地存儲的全部內容 */ api.clear = function () { storage.clear(); } } else if (globalStorageName in win && win[globalStorageName]) { // HTML5中的localStorage替換了原來的globalStorgae // 1. 拿到本地存儲的對象(這是一個Json對象)[Firefox瀏覽器] storage = win[globalStorageName][win.location.hostname]; api.set = function (key, value) { storage[key] = value; } api.get = function (key) { return storage[key] && storage[key].value; } api.remove = function (key) { // delete用來刪除一個對象的屬性。 delete storage[key]; } api.clear = function () { for (var key in storage) { delete storage[key]; } } } else if (doc.documentElement.addBehavior) { // 若是能夠給一個對象添加行爲的話 // 單獨定義一個獲取本地存儲的對象storage function getStorage() { // 若是已經獲取到了Storage對象的話 if (storage) { return storage; } storage = doc.body.appendChild(doc.createElement('div')); storage.style.display = 'none'; // userData 64KB IE專用 storage.addBehavior('#default#userData'); // 這個是微軟自定義的一個本地存儲,相比之下有更大的容量 storage.load(localStorageName); return storage; } api.set = function (key, value) { var storage = getStorage(); // 設置屬性 storage.setAttribute(key, value); // 保存屬性信息 storage.save(localStorageName); } api.get = function (key) { var storage = getStorage(); return storage.getAttribute(key); } api.remove = function (key) { var storage = getStorage(); storage.removeAttribute(key); // 移出數據以後記得保存一下數據 storage.save(localStorageName); } api.clear = function () { // 1. 獲取Storage對象 var storage = getStorage(); // 2.獲取storage對象存儲的全部屬性信息 var attributes = storage.XmlDocument.documentElement.attributes; storage.load(localStorageName); // 3. 遍歷全部的屬性信息,並從本地移出數據 [].slice.call(attributes).forEach(function (element) { storage.removeAttribute(element.name); }) // 4. 移出完畢以後,開始保存信息到本地存儲 storage.save(localStorageName); } return api; } // 把當即函數裏面的私有成員暴露出去(若是在當即函數內部不暴露出去須要使用的成員,在外部是沒法訪問到內部的私有成員變量的) xframe.storage = api; })(xframe); })(xframe);