建立起總的架構
創建一個沙箱保護私有變量,避免全域污染 (function(window){})(winow)
把 window 作為形參傳進去的兩個優點:
1、減少作用域的搜尋
2、提高壓縮效率
建立一個建構函數,function Guoqi(){} // 作用:每一個 Guoqi 物件都是一個偽陣列
在原型物件上建立一個 init 物件在構造函數中直接 return new Guoqi.prototype.init();
就可以成功的隱藏 new 關鍵字,
但是最關鍵的一步,一定要將 Guoqi.prototype.init.prototype = Guoqi.prototype的實例都可以用 Guoqi.prototype 原型上的方法啦
最後把 window.Guoqi = window.Q = Guoqi 把 Guoqi 和 Q 作為介面掛在window上,方便在外部直接調用外部直接調用一個對象,放在原型中,預設值為0,確保 Q 對象為一個偽數組
用替換原型對象的方法 Guoqi.prototype = {}; 注意:constructor 屬性要指向建構子本身
為了方便我們書寫,不用每次創建的實例都帶有new 關鍵字,有一個小技巧
init 方法的充實, Q 對像中可以傳入多個參數需要進行判斷
如果以'
否則(傳入的是DOM元素)的話將作為選擇器用qsa 的方法,獲取到對象
傳入的是字符串的話,那麼將分情況
傳入的是DOM 物件的話 selector.nodeType 那麼直接存放到物件中 this[0] = selector;this.length = 1; 保證其是一個偽數組
傳入的是Q 物件的話,selector.conoror .name = Guoqi (因為Q 物件都是用 Guoqi 建構函數創建出來的),直接返回 selector
傳入的函數 function 的話,暫時先不做操作 typeof selector === "function",jQuery中的入口 例如函數 $(function(){})
傳入的是數組(偽數組)的話,selector.length >=0 , 把數組的值,依次傳入到this中
否則的話不管傳入的是什麼,this[0] = selector; this.length = 1;
這樣就確保,不管 Q( ) 傳入的什麼,我們都得到了一個__偽數組__
添加擴展方法的功能 extend( obj ) 在extend中傳入一個物件
Guoqi.extend = Guoqi.prototype.extend = function(){ } 利用聯等符號,代表分別為建構函數,與建構函數原型新增extend方法
extend 所使用的混入繼承的原理,把obj 的功能拷貝到 this 物件上去,(注意:此時兩個this的指向不同)
Guoqi.extend({}) 給建構函數擴充的方法稱為靜態方法,作為工具性的方法使用(each,map,next...)
給建構函式新增的 each 方法,要注意:this指向遍歷的目前對象,而且一旦傳回值為 false 的話,就停止遍歷;即
if( callback.call ( array[i], i, array[i] ) === false ) break;map 方法this指向的是window,只有當返回值不為 undefined 或 null 的時候,才往返回的新數組中添加已遍歷的元素,否則的話即:if( callback( array[i], i ) != undefined){ newArr.push( callback( array[i], i ) ) }; 最後__return newArr__;
給實例新增的方法,語法:Q().each(function(){}); 所以要把 each 方法轉給Q實例對象,例如appendTo, each, map
如果傳入了一個 index ,那麼返回 index 對應值的DOM元素
支持傳入負數,那麼將從後面倒著往前數,例如:傳入-1,那麼對象的就是 length- 1 的DOM元素
toArray 功能:將取得的Q物件(偽數組) 傳回為一個 DOM 元素的真數組
get(index) 功能:如果沒有傳入 index 索引,那麼將傳回一個DOM元素的真元素的真數組
DOM 操作模組
首先在沙箱中定義一個 function parseHtml( str ){} 創建根據傳入的標籤,創建Q對象
創建的時候,可以隨意創建一個把標籤,把我們想要創建的標籤,把我們想要創建的標籤,把我們想要創建的標籤,把我們想要創建的標籤標籤,加入div中,div.innerHTML = str;
然後把div的 childNodes 返回就可以,但是要__注意__,childNodes會隨著我們渲染在頁面上之後,length會改變
所以,我們需要建立一個空數組,把childNodes 裡面的DOM元素,依序放到數組中,
那麼,parseHtml 函數,返回的就是一個__真的數組__,在init方法中'
appendTo(selector) 實例實例方法功能:將this對象追加到selector的子元素中
該方法也要根據傳入的對象, DOM元素,DOM對象,Q對象,偽數組... 做出判斷★(所以前提是init方法的完善)
完善之後,先把selector 轉換為一個Q物件---> Q(selector)
建立一個陣列objQ,和一個變數tempDom,(為了下面的回傳值考量)
對this 和selector 進行嵌套循環,並且要對this[i]進行克隆,注意克隆的數量,要保證本體為最後一個,不要克隆多了
並且不斷將this 的值賦值給tempDom保存起來, tempDom = j === Q(selector).length-1 ? this[i]:this[i].cloneNode(true)
再把 tempDom 加到數組objQ中去,
考慮回傳值的問題,我們的回傳值應該是,加入selector中所有的this元素,包括克隆的,所以objQ中的元素,便是我們應該返回的值
這樣的話,我們的鍊式編程的鍊便遭到了破壞,所以我們就要創建一個_ _end()__方法,來返回上一個的鏈對象
這裡的話,我們就需要將當前this保存下來,以便於調用end()方法可以返回
即:將objQ轉換為Q對象,給其加入一個preObj屬性指向this,然後把Q(obj)return出去
end() 實例方法,當鏈遭到破壞的時候,呼叫一下可以找到上一級的鏈(只能找到一級)
我們上面的工作已經做得非常充實,所以end()方法只要 return this.preObj || this ; (注意:如果沒有上一級的話,把自己return出去)
pushStack()實例方法, 就是appendTo方法後面保存上一層物件的內容,
因為可能有很多方法會遭到鏈破壞,所以我們封裝一個方法,實現復用的效果
即:
function pushStack(arr){ var newObjQ = Q( arr); newObjQ.preObj = this; return newObjQ; }
注意:該方法雖然實例不會直接調用,但是,因為在appendTo方法中應該是this調用的,才能找到this的上一級鏈對象(雖然之後可以藉用call/apply方法實現,但是這樣的話就更麻煩了);
就是說,不能直接當私有函數(例如parseHtml)放到沙箱中,這樣的話上一級對象就是window,實現起來更麻煩
prependTo append preprend remove ... 實例方法實作都是和appendTo一樣的原理
實作方法: 本來用this.nextElementSibling 就可以實現,但是有相容問題
所以我們給Guoqi構造函數擴展了一個next(dom)方法,循環遍歷dom元素,尋找其下一個節點,若是nodeType === 1的話,直接return dom;否則的話return null;
即
實作 this.each(function(){ this. parentNode.removeChild( this ); })
實作 Q(selector).prependTo(this); return this; 鏈不會遭到破壞
實作 Q(selector).appendTo(this); return this; 鏈不會遭到破壞
實作 selector[j].insertBefore( tempDom, selector[j].firstChild );
注意:此方法和appendTo方法一樣,鏈會遭到破壞
prependTo 功能物件:thisselector子元素的最前面
append this物件的子元素加入selector的物件
prepend this物件的子元素最前面加上selector的物件
remove 方法找到this中父節點刪除其中的所有this元素,包括this本身
next 方法找到this物件中緊鄰的下一個元素(而不是所有的同輩(兄弟)元素) 鏈會遭到破壞
function next(dom){ while( dom = dom.nextSibling ){ if( dom.nodeType === 1 ){ return dom; } } return null; }
然後next實例方法,只需要 return pushStack(this.map(function (v){ return Guoqi.next(v) }) );
filter(selector) 方法為this實例物件和selector找到相同的元素
準備好一個ret空數組,對this實例和Guoqi(selector )進行巢狀迴圈遍歷,若this[i] == Guoqi(selector)[j],就加入到陣列ret中去
最後把ret 這個陣列轉換為實例物件回傳即:return Guoqi(ret);
unique() 功能:去除相同的元素
準備一個空數組arr, 呼叫this.toArray()方法,把實例物件轉換為真正的陣列thisArr,然後進行循環遍歷
if( arr.indexOf( thisArr,然後進行循環遍歷
if( arr.indexOf( thisArr,然後進行循環遍歷
if( arr.indexOf( thisArr,然後進行循環遍歷
🎜if( arr.indexOf(thisAthis (i) )== -1 ){ arr.push(thisArr[i]) };🎜🎜然後return Guoqi( arr );🎜🎜children(selector): 功能:若沒有傳入參數就查找到所有的子元素(僅限於兒子輩的),若傳入了參數,就找到與selector相符的子元素🎜原理:找到this实例对象中的所有的子元素,组成一个数组,找到selector中的所有元素 组成一个数组,然后寻找两个数组之间相同的元素即可(调用filter方法),注意:涉及到了unique去重的问题
首先
var subList = this.map(function(v){ return Guoqi.map(v.children,function(v1){ return v1 }) }); // 此时 sunList是一个二维数组,即:数组里面套数组
需要把subList 合成一个 一维数组,借用concat方法:var newSub = [].concat.apply([],subList);
然后对 newSub进行去重(需要将数组转换为Q对象才能调用unique方法), var subQ = Guoqi(newSub).unique();
需要对selector进行判断,如果 selector存在的话,那么 return subQ.filter( Guoqi(selector) ); 如果不存在的话,那么直接 return subQ;
find(selector) : 功能:find一般是找指定的元素,在this实例对象的所有后代元素中找,所以一般都传入参数
find方法和children方法一样,只需要把
var subList = this.map(function(v){ return Guoqi.map(__v.querySelectorAll(selector)__,function(v1){ return v1 }) });
用v.querySelectorAll(selector); 来找到所有的后代元素(应该还有更好的办法,欢迎来补充)
nextAll 工具方法 跟next方法如出一辙:
即
function next(dom){ while( dom = dom.nextSibling ){ if( dom.nodeType === 1 ){ return dom; } } return null;}
next,prev,nextAll,prevAll 方法 都常会在 封装实例方法中用到,所以可以封装为工具方法; prevAll,prev 两个方法和next,nextAll 原理一样,所以不再详细赘述
注意:nextAll,prevAll,children 这些链都遭到了破坏,不过可以使用end恢复
nextAll 实例方法 既然有了上面的nextAll的工具方法,那么相对应的实例方法就简单了很多,
只需要把this实例对象的每一个dom元素调用 nextAll(dom),组成一个数组(用map方法简单),
然后把这些数组用concat组合在一起进行__去重(unique)__,进而转换为Guoqi对象即可
siblings 实例方法
有了prevAll 和 nextAll方法,siblings就变得简单多了,只要两者组合在一起即可
var nextSiblings = this.nextAll().toArray(); var prevSiblings = this.prevAll().toArray();
返回 Guoqi( prevSiblings.concat( nextSiblings ) );
事件操作 模块
on 事件的实现 (我们选择先实现on事件,是因为on事件是通用的,实现on事件之后,其他的具体事件都可以用on事件来实现)
on 事件 语法:Guoqi.fn.extend(on: function( 事件列表,callback ){ });
事件列表 可以有实现多个事件 中间用空格隔开
这就意味着 我们要对this实例对象进行遍历,也要对事件列表进行遍历
添加事件的时候,我们选择使用 addEventListener("事件名",callback);
各个事件的实现
首先我们可以从控制台打印出来所有的事件
创建一个DOM对象div,for( var k in div ){ if( k.slice(0,1) === "on" ){ arr.push(k) } }; 这样就把所有事件放到arr数组中去了
循环遍历数组中每个值,只保留事件名的部分,即:v = v.slice(2);
然后添加在原型上:Guoqi.fn[v] = function(callback){ return this.on(v,callback) };
css 样式操作模块的实现
首先我们需要考虑css的参数情况 语法:Guoqi.fn.css = function( name,value){ }
字符串( typeof name === "string" ),即:需要获取实例对象的值:一般情况下,我们获取的是实例对象中第一个元素的值
(this[0] / this.get(0)) 可以直接 return this[0].style[name] ,但是值得注意的是,这样只能获取行内样式的值
所以 我们还需要 || window.getComputedStyle(this[0])[name]; 但是getComputedStyle在低版本的IE浏览器中(currentStyle)不支持,
如果考虑严谨的话,可以封装一个getStyle获取样式的函数
只有一个参数 即:value == undefined 可能是对象,也可能是一个字符串
function getStyle(dom,style){ return dom.currentStyle?dom.currentStyle[style]: window.getComputedStyle(dom)[style]}
这样的话,只需要循环遍历this对象,即:this[i].style[name] = value; 并且要把this实例对象返回回去,实现链式编程
对象 不仅需要循环遍历this实例对象,还需要遍历name这个键值对的值:做到for( var k in name ){ this[i].style[k] = name[k] };
注意要返回 this实例对象,便于链式编程
有两个参数 name value 设置单个样式
属性 样式操作模块的实现 (与css实现原理相似)
hasClass(className) 判断实例对象 有没有该类名,
实现:我们需要分别对this实例对象进行遍历,和他们的所有的类名进行遍历,
为了方便操作,需要把实例对象的类名转换为数组,并去除前后空格(遍历检查),然后使用__indexOf()__方法,若为-1,则返回false, >=0或者!=-1则为true;
即:var classNameValues = this(指的是实例对象中的dom).className.trim().split(" "); classNameValus.indexOf(className) >=0 --->true
可以使用some方法,(只要有一个满足条件直接放回true) 即:return this.toAArray().some(function(v){ return v.trim().split(" ")indexOf(className) >= 0 };
addClass(className) 添加类名
又需要分清况讨论了,若是没有类名:即classNameValues.length == 0,则直接添加 this.className = className;
若是有class,但是没有该类名,则需要追加;即:if( classNameValues.indexOf(className) == -1 ){this.className += " "+classNaame};__(注意要用空格分隔开)__;
若是已经有该类名了,则什么都不需要做(不能重复添加)
addClass可是直接用数组来实现简单一些:只要if( classNameValues.indexOf(className) == -1 ){ classNameValues.push(className) };this.className = classNameValues.join(" ");
removeClass(className) 删除类名
就是把指定的类名给删除掉,需要进行循环遍历所有类名数组 classNameValues,然后用splice来把指定的类名从数组中给截取掉;
即var index;(来记录查到指定类名的索引)
js while( (index= classNameValues.indexof(className))>=0 ){ classNameValues.splice(index,1);} this.className = classNameValues.join(" ");
还可以使用 map方法; 即:this.className = classNameValues.map(function(v){if(v != className){return v;}}).join(" "); 利用map方法产生一个新数组,简单一些
toggleClass(className) 切换类名
直接进行判断
if(this.hasClass(className)) { this.removeClass(className) }else{ this.addClas s(className) };
对上面的代码实现复用,减少代码冗余; 如果有该类名的话,直接删除,没有类名的话,就添加
attr(name,value) 对属性样式的设置 和css 原理一样:还是要分情况:
一个参数时,类型为字符串,获取属性值:用getAttribute(name);
一个参数时,类型为对象,数值多个属性值 ,循环遍历该对象用setAttribute(k,name[k])来进行设置
两个参数时,设置单个属性值,直接进行设置即可:setAttribute(name,value);
prop(name,value) 与上述情况一样,分类进行考虑,
但是注意的是。prop针对的是input标签这些单属性的值,值为布尔类型,例如disabled,checked,selected
设置和获取的时候不用setAttribute,getAttribute,直接进行赋值即可,this[name] = value;
注:如果对其进行赋值,例如disabled,不论赋值为true还是false,都不可编辑,除非移除该属性removeProp
removeAttr与removeProp,个人认为实现原理一样,都是使用removeAttribute
即:遍历this实例对象,然后进行 this.removeAttribute(name);
入口函数,即init方法中selector传入的数函数的情况
方法一:直接使用window.addEventListener("load",selector); 可以实现累加,开辟不同的入口函数,互不影响
方法二:利用函数的技巧:记录下来window.onload的值,进行判断如果是一个函数的话,则他执行,在执行selector传入的,否则的话,直接把selector赋值给window.onload
即:
var oldFn = windiw.onload ; if(typeof oldFn == "function"){ window.onlad = function(){ oldFn();selector(); }}else{ window.onload = selector(); }
方法三:利用数组的技巧:建立一个私有数组,var loads = []; 直接把一个个的selector给push到数组中去,
然后定义一个
window.onload = function(){ Guoqi.each(loads,function(){ this(); }) } // 把数组中的selector依次执行

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

本教程向您展示瞭如何將自定義的Google搜索API集成到您的博客或網站中,提供了比標準WordPress主題搜索功能更精緻的搜索體驗。 令人驚訝的是簡單!您將能夠將搜索限制為Y

因此,在這裡,您準備好了解所有稱為Ajax的東西。但是,到底是什麼? AJAX一詞是指用於創建動態,交互式Web內容的一系列寬鬆的技術。 Ajax一詞,最初由Jesse J創造

本文系列在2017年中期進行了最新信息和新示例。 在此JSON示例中,我們將研究如何使用JSON格式將簡單值存儲在文件中。 使用鍵值對符號,我們可以存儲任何類型的

利用輕鬆的網頁佈局:8 ESTISSEL插件jQuery大大簡化了網頁佈局。 本文重點介紹了簡化該過程的八個功能強大的JQuery插件,對於手動網站創建特別有用

核心要點 JavaScript 中的 this 通常指代“擁有”該方法的對象,但具體取決於函數的調用方式。 沒有當前對象時,this 指代全局對象。在 Web 瀏覽器中,它由 window 表示。 調用函數時,this 保持全局對象;但調用對象構造函數或其任何方法時,this 指代對象的實例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。這些方法使用給定的 this 值和參數調用函數。 JavaScript 是一門優秀的編程語言。幾年前,這句話可

jQuery是一個很棒的JavaScript框架。但是,與任何圖書館一樣,有時有必要在引擎蓋下發現發生了什麼。也許是因為您正在追踪一個錯誤,或者只是對jQuery如何實現特定UI感到好奇

該帖子編寫了有用的作弊表,參考指南,快速食譜以及用於Android,BlackBerry和iPhone應用程序開發的代碼片段。 沒有開發人員應該沒有他們! 觸摸手勢參考指南(PDF)是Desig的寶貴資源


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver Mac版
視覺化網頁開發工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3漢化版
中文版,非常好用

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境