1 (function(window,undefined){ 2 var arr = [], 3 push = arr.push, 4 slice = arr.slice; 5 //首先要做的就是封裝一個parseHtml函數 6 //要註意的是,parthHtml要放在一個自調用函數內 7 var... ...
1 (function(window,undefined){ 2 var arr = [], 3 push = arr.push, 4 slice = arr.slice; 5 //首先要做的就是封裝一個parseHtml函數 6 //要註意的是,parthHtml要放在一個自調用函數內 7 var parseHtml = (function(){ 8 //做一個模板 9 var div = document.createElement('div'); 10 //這個函數是用來創建標簽 11 function parseHtml(html){ 12 var res = []; 13 //這邊是將具有html語義的字元串放在模板div中,div裡面的就有這些標簽了 14 div.innerHTML = html; 15 //這個時候div里有標簽元素,迴圈遍歷div裡面的元素 16 for(var i = 0;i < div.childNodes.length;i++){ 17 //這邊是將div模板裡面的元素一一放到res數組裡,因為這邊的div.childNodes[i]是單個元素,可以直接push進數組 18 res.push(div.childNodes[i]); 19 } 20 //完成之後,將div裡面的元素清除掉,方便下一次使用 21 div.innerHTML = ''; 22 //將這個數組返回 23 return res; 24 } 25 return parseHtml; 26 })(); 27 //封裝insertBefore方法 28 function prependChild(parent,element){//傳入的元素是父元素,要插入的元素 29 var first = parent.firstChild;//將父元素中的第一個元素取出來 30 parent.insertBefore(element,first);//使用insertBefore方法將element插入到父元素的第一個子元素的前面 31 } 32 //封裝cc函數 33 function cc(html){ 34 //這邊返回的是cc原型對象裡面的一個方法創建出來的實例 35 return new cc.fn.init(html); 36 } 37 //這邊是為了後面書寫方便 38 cc.fn = cc.prototype = { 39 constructor : cc, 40 length : 0, 41 //這兩個屬性要加到原型上 42 selector: '',//這兩個屬性在init所創建的實例之中還會用到,所以要加到這個原型上面 43 type : 'cc', 44 init : function(html){//這個函數的作用就是:1.創建實例;2.在這個函數裡面對所傳入的參數進行一個判斷 45 this.events = {}; 46 if(html == ''){//這邊判斷傳入的參數是不是一個空字元串 47 return;//如果傳入的是一個空字元串,那麼就直接返回this,註意,這邊雖然沒寫返回this,但是他是預設的 48 }else if(cc.isString(html)){//這邊是當傳入的html是字元串的時候 49 if(/^</.test(html)){//這邊的html是標簽組成的字元串 50 push.apply(this,parseHtml(html)); 51 }else{//這邊的html是選擇器字元串 52 //這邊的this指的是通過:cc.prototype.init這個函數創建出來的實例 53 push.apply(this,select(html)); 54 //將這個實例的selector設置成html 55 this.selector = html; 56 } 57 }else if(html && html.type === 'cc'){//這邊是當傳入的html是一個cc對象的時候 58 push.apply(this,html); 59 this.selector = html.selector; 60 this.events = html.events; 61 }else if(html && html.nodeType){//這邊是當傳入的是一個dom對象的時候,因為dom對象都有nodeType屬性 62 this[0] = html; 63 this.length = 1; 64 }else if(typeof html === 'function'){//這邊是當傳入的是一個函數的時候,進入裡面 65 var oldfunc = onload;//將原來onload裡面的函數取出來 66 if(typeof oldfunc === 'function'){ 67 onload = function(){ 68 oldfunc(); 69 html(); 70 } 71 }else{ 72 onload = html; 73 } 74 } 75 } 76 }; 77 //將封裝好的select方法放到這裡面 78 var select = (function () { 79 //這是一個異常與捕獲的代碼,它表示的意思是:如果push方法出現了錯誤那麼就需要重寫push方法 80 try { 81 //這邊是自己模擬一個場景,來使用系統的push方法,如果可以實現的話就證明系統支持push方法 82 //這種方法是系統能力檢測中的方法功能檢測 83 var div = document.createElement( 'div' ); 84 div.innerHTML = '<p></p>'; 85 var arr = []; 86 push.apply( arr, div.getElementsByTagName( 'p' )); 87 } catch ( e ) { 88 //這邊是當try裡面的push方法不執行的時候,會進入這裡面 89 //在這裡面將push重新定義了一下,將其變為一個對象,這個對象裡面有一個push方法 90 var push = { 91 //將apply變成了push對象裡面的一個方法 92 apply: function ( array1, array2 ) { 93 for ( var i = 0; i < array2.length; i++ ) { 94 //註意這邊的賦值 95 array1[ array1.length++ ] = array2[ i ]; 96 } 97 } 98 }; 99 } 100 // 正則表達式 101 //這句正則表達式是為了匹配系統中是否有自帶的方法 102 var rnative = /\{\s*\[native/; 103 var rtrim = /^\s+|\s+$/g; 104 //這個是為了匹配出 Id 類名 通配符 標簽名 105 // 1 2 3 4 106 var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+)|(\*)|(\w+))$/; 107 // 基本函數, support 對象, 驗證 qsa 與 byclass 108 var support = {}; 109 //基本函數裡面的一個屬性,實質上是為了看一下系統中是否有該方法(使用正則來判斷) 110 support.qsa = rnative.test( document.querySelectorAll + '' ); 111 //同上 112 support.getElementsByClassName = rnative.test( document.getElementsByClassName + '' ); 113 support.trim = rnative.test( String.prototype.trim + '' ); 114 support.indexOf = rnative.test( Array.prototype.indexOf + '' ); 115 // 基本方法 116 //封裝了getElementsByClassName函數,這是為瞭解決相容問題 117 //傳入兩個參數,一個是className,另一個是node-->這個node指的是從頁面上的node元素開始查找這個className 118 function getByClassName ( className, node ) { 119 //如果沒有傳入node的話就給它一個預設值:document 120 node = node || document; 121 //聲明一些變數 122 var allElem, res = [], i; 123 //首先做判斷,如果系統有這個方法會使用系統的 124 if ( support.getElementsByClassName ) { 125 //直接使用定義的push方法 126 return push.apply(res,node.getElementsByClassName( className )); 127 } else { 128 allElem = node.getElementsByTagName( '*' ); 129 for ( i = 0; i < allElem.length; i++ ) { 130 if ( allElem[ i ].className === className ) { 131 res.push( allElem[ i ] ); 132 } 133 } 134 return res; 135 } 136 } 137 // 自定義實現 trim 方法,該方法是將字元串中的指定的東西去掉 138 var myTrim = function ( str ) { 139 // 表示兩端去空格, 然後返回去除空格的結果 140 if ( support.trim ) { 141 return str.trim(); 142 } else { 143 // 自定義實現 144 //這邊是將rtrim轉換成空字元串,即將空格去掉了 145 return str.replace( rtrim, '' ); 146 } 147 } 148 //這邊封裝的是indexOf方法,傳入三個參數,數組,要搜索的東西,要搜索的東西在數組裡面的開始索引(從第幾個開始找) 149 var myIndexOf = function ( array, search, startIndex ) { 150 //首先將索引賦值,如果傳入了索引,那麼就讓開始的索引等於它,如果沒有傳入那麼就讓它從零開始找 151 startIndex = startIndex || 0; 152 //這邊還是先判斷系統有沒有這種方法 153 if ( support.indexOf ) { 154 //這裡表示系統有這種方法,那麼就直接使用就OK了,將結果返回 155 return array.indexOf( search, startIndex ); 156 } else { 157 //如果沒有的話,我們就自己動手封裝一個 158 //對這個數組進行一個遍歷,遍歷的初始值就是從startIndex開始 159 for ( var i = startIndex; i < array.length; i++ ) { 160 //判斷一下,如果數組裡面有值與要查詢的值相等,那麼就返回這個索引值 161 if ( array[ i ] === search ) { 162 return i; 163 } 164 } 165 //當遍歷完成之後,如果還是沒有找到的話,就返回-1 166 return -1; 167 } 168 } 169 //封裝一個去重的函數,傳入的參數是一個數組 170 var unique = function ( array ) { 171 //聲明一個空數組,用於接收去重之後的元素 172 var resArray = [], i = 0; 173 //對傳入的數組進行一個遍歷 174 for ( ; i < array.length; i++ ) { 175 //做一個判斷,如果說resArray裡面沒有arr裡面的元素,則將arr裡面的元素放到resArray裡面 176 //註意,這邊使用的是之前封裝好的myIndexOf方法 177 if ( myIndexOf( resArray, array[ i ] ) == -1 ) { 178 //使用的是前面封裝好的push方法 179 resArray.push( array[ i ] ); 180 } 181 } 182 //將這個數組返回 183 return resArray; 184 } 185 //這邊封裝的是四種基本選擇器,ID選擇器,類選擇器,通配符選擇器,標簽選擇器 186 function basicSelect ( selector, node ) { 187 //這邊的node指的是要在哪一個下麵去尋找selector 188 node = node || document; 189 var m, res; 190 if ( m = rbaseselector.exec( selector ) ) { 191 if ( m[ 1 ] ) { // id選擇器 192 res = document.getElementById( m[ 1 ] ); 193 if ( res ) {//如果res不是一個空的話,進入 194 return [ res ]; 195 } else {//當res為空的話返回一個空數組 196 return []; 197 } 198 } else if ( m[ 2 ] ) { // class選擇器 199 return getByClassName( m[ 2 ], node ); 200 } else if ( m[ 3 ] ) {//通配符選擇器 201 return node.getElementsByTagName( m[ 3 ] ); 202 } else if ( m[ 4 ] ) {//標簽選擇器 203 return node.getElementsByTagName( m[ 4 ] ); 204 } 205 } 206 return []; 207 } 208 //封裝一個組合選擇器,這裡面的標簽使用逗號隔開的 209 function select ( selector, results ) { 210 results = results || []; 211 var m, temp, selectors, i, subselector; 212 //這邊是如果傳入的selector不是一個字元串的話,那麼返回空數組 213 if ( typeof selector != 'string' ) return results; 214 // 表明參數都沒有問題, 接下來就是如何選擇 215 // 首先判斷 qsa 是否可用 216 // 然後再 一步步的 自己實現 217 if ( support.qsa ) {//如果系統有querySelectorAll方法,那麼就使用 218 push.apply( results, document.querySelectorAll( selector ) ); 219 } else { 220 // 不存在再來考慮自己實現 221 //首先將傳入的參數以逗號隔開,放到一個數組裡面 222 selectors = selector.split( ',' ); 223 // 迴圈遍歷這個數組 224 for ( i = 0; i < selectors.length; i++ ) { 225 //在這個迴圈裡面對數組裡面的每一個元素進行一個去除空格的操作,保證數據是我們想要的形式 226 subselector = myTrim( selectors[ i ] ); 227 // 接下來就是 處理 subselector,使用正則進行判斷 228 if ( rbaseselector.test( subselector ) ) { 229 // 基本選擇器 230 //如果匹配到了就將匹配到的傳入到基本的四種選擇器函數只中,返回一個數組,將這個數組進行一個push 231 push.apply( results, basicSelect( subselector ) ); 232 } else { 233 //如果匹配不到那麼就使用 select2 函數 234 select2( subselector, results ); 235 } 236 } 237 } 238 // 返回 result註意,這個數組要進行一個去重操作 239 return unique( results ); 240 } 241 //封裝一個後代選擇器的函數,傳入兩個參數 242 function select2 ( selector, results ) { 243 results = results || []; 244 //將這個參數以逗號隔開 245 var selectors = selector.split( ' ' ); 246 //聲明一個數組,這個數組用於存放元素,以及一個node數組,這個數組用於存放從哪一個元素開始找 247 var arr = [], node = [ document ]; 248 for ( var j = 0; j < selectors.length; j++ ) { 249 for ( var i = 0; i < node.length; i++ ) { 250 //因為這邊尋找的是後代選擇器,所以只要找到最後面的並將其返回就可以了 251 push.apply( arr, basicSelect( selectors[ j ], node[ i ] )); 252 } 253 //在結束的時候將arr裡面的值全部給node,要註意此時node裡面的值的作用是什麼 254 node = arr; 255 //將arr清空 256 arr = []; 257 } 258 //在最後將最後一次獲取到的node值賦給results 259 //這裡面的值是最後一次獲取到的元素,也就是說是要獲取的子代元素中的的最後一個元素 260 push.apply( results, node ); 261 return results; 262 } 263 return select; 264 })(); 265 //讓init這個方法的原型與cc這個構造函數的原型相等,這樣init的原型裡面就有init的方法 266 cc.fn.init.prototype = cc.fn; 267 //給cc以及cc原型添加一個混入的方法 268 cc.extend = cc.fn.extend = function extend(obj){ 269 for(var k in obj){ 270 this[k] = obj[k]; 271 } 272 } 273 //給cc混入方法,這些方法都是靜態方法 274 cc.extend({ 275 //給cc混入一個isString方法 276 isString : function(html){ 277 return typeof html === "string"; 278 }, 279 //給cc混入一個each方法 280 each : function(arr,fnc){ 281 var i; 282 if(arr instanceof Array || arr.length >= 0){ 283 for(i = 0; i < arr.length; i ++){ 284 fnc.call(arr[i],i,arr[i]); 285 } 286 }else { 287 for( i in arr){ 288 fnc.call(arr[i],i,arr[i]); 289 } 290 } 291 return arr; 292 }, 293 map : function(arr,fnc){ 294 var i,tmp,res=[]; 295 if(arr instanceof Array || arr.length >= 0){ 296 for(i =0;i<arr.length;i++){ 297 //這邊的tmp的值是這邊fnc的返回值,如果說fnc這個函數裡面沒有返回值的話(return),那麼tmp就是undefined 298 tmp = fnc(arr[i],i); 299 if(tmp != null){ 300 res.push(tmp); 301 } 302 } 303 }else{ 304 for(i in arr){ 305 tmp = fnc(arr[i],i); 306 if(tmp != null){ 307 res.push(tmp); 308 } 309 } 310 } 311 return res; 312 }, 313 //獲取到樣式 314 getStyle : function(dom,styleName){ 315 if(dom.currentStyle){ 316 return dom.currentStyle[styleName]; 317 }else{ 318 return window.getComputedStyle(dom)[styleName]; 319 } 320 }, 321 getText : function(dom,list){ 322 var node = dom.childNodes; 323 for(var i = 0;i < node.length;i ++){ 324 if(node[i].nodeType == 3){ 325 list.push(node[i].nodeValue); 326 } 327 if(node[i].nodeType == 1){ 328 cc.getText(node[i],list); 329 } 330 } 331 } 332 }); 333 //核心方法 334 cc.fn.extend({ 335 //將調用該方法的對象變成數組模式 336 toArray : function(){ 337 //法一: 338 /*var res = []; 339 for(var i = 0;i < this.length; i ++){ 340 res.push(this[i]); 341 } 342 return res;*/ 343 //法二: 344 //這句話的意思是:this.slice(0);--->this指的是實例對象,調用slice將其從一個偽數組變成一個數組 345 return slice.call(this , 0); 346 }, 347 //get方法是通過索引值來返回調用該方法的對象/數組裡面的對應的值 348 get : function(index){ 349 if(index === undefined){ 350 return this.toArray(); 351 } 352 return this[index]; 353 }, 354 //這邊的eq返回的是dom對象,這是與get方法的區別,簡單地說就是eq返回的是一個init對象,而get返回的是一個元素 355 eq : function(num){ 356 var dom; 357 if(num >= 0){ 358 dom = this.get(num); 359 }else{ 360 dom = this.get(this.length + num); 361 } 362 return cc(dom); 363 }, 364 each : function(fnc){ 365 return cc.each(this,fnc); 366 }, 367 map : function(fnc){ 368 return cc.map(this,fnc); 369 } 370 }); 371 //對DOM的操作方法 372 cc.fn.extend({ 373 //給cc的原型混入一個appendTo方法,註意這邊的原型是與init的原型是一至的 374 //首先要明白,appendTo方法是將appendT之前的元素添加到appendTo的括弧里 375 appendTo : function(selector){ 376 //首先聲明一個ccObj對象,這個對象是和this一樣,是init的實例對象 377 //當我們將selector傳入其中時,它會解析出來傳入的是一個什麼樣的參數 378 //這邊的selector參數,其實就是appendTo括弧裡面的內容 379 var ccObj = this.constructor(selector); 380 //這邊聲明一個新的init的實例對象,這是為了仿照jQuery裡面鏈式原理的寫法 381 //這邊執行完該方法之後,返回的是一個dom對象 382 var newObj = this.constructor(); 383 //這邊迴圈遍歷this,這個this指的就是appendTo前面的內容 384 for(var i = 0;i < this.length;i ++){ 385 //這邊迴圈遍歷的是解析了傳入參數selector之後得到的一個對象ccObj 386 for(var j = 0;j < ccObj.length;j ++){ 387 //聲明一個變數temp 388 var temp; 389 if(j == ccObj.length-1){//當j迴圈到最後一個的時候,進入這個迴圈 390 //將this[i]賦值給temp 391 temp = this[i]; 392 }else{ 393 //這種情況就是當j值還沒有到最後一個的時候 394 temp = this[i].cloneNode(true); 395 } 396 //將temp裡面的值,放到之前創建的newobj中,這是為了仿照jQuery裡面的鏈式結構 397 push.call(newObj , temp); 398 //將temp添加到ccObj中,temp裡面存放的是this偽數組裡面的值 399 ccObj[j].appendChild(temp); 400 } 401 } 402 //這個是:即使完成了這個appendTo操作,返回值依舊是一個cc對象 403 return newObj; 404 }, 405 //首先要明確的是:append方法是將append括弧裡面的加到append前面的元素中 406 append: function(selector){ 407 //還是一樣,先聲明一個cc對象,這個對象裡面是將傳入的參數進行解析 408 var ccObj = this.constructor(selector); 409 //這個迴圈的是創建的cc對象 410 for(var i = 0;i < ccObj.length;i ++){ 411 //這個迴圈的是this這個偽數組 412 for(var j = 0;j < this.length;j ++){ 413 var temp; 414 if(j == this.length-1){//這邊的做法可以參照appendTo的方法封裝 415 temp = ccObj[i]; 416 this[j].appendChild(temp); 417 }else{ 418 temp = ccObj[i].cloneNode(true); 419 this[j].appendChild(temp); 420 } 421 } 422 } 423