jQuery鏈式調用與show知識淺析,jquery鏈式
上篇文章給大家介紹了jQuery的架構,有關jquery的基礎知識可以參考下。
jQuery使用許久了,但是有一些API的實現實在想不通。下面將使用簡化的代碼來介紹,主要關注jQuery的實現思想。
相較於上一篇,代碼更新了:21~78
(function(window, undefined){function jQuery(sel){return new jQuery.prototype.init(sel);}jQuery.prototype = {constructor: jQuery,init: function(sel){if(typeof sel === 'string'){var that = this;var nodeList = document.querySelectorAll(sel);Array.prototype.forEach.call(nodeList, function(val, i){that[i] = val;})this.selector = sel;this.length = nodeList.length;}},show: function(){Array.prototype.forEach.call(this, function(node){//if(node.style) continue; //textnode沒有style//刪除style上的display:nonevar display = node.style.display;if(display === 'none'){//dispaly置為空白後,css如果有display則css的生效//否則預設的生效node.style.display = '';}//元素display值為非預設值情況,需要還原為oldDisplay:div->display:inline-block//或 檢測css上的display是否為noneif(node.style.display==='' || isHidden(node)){//有oldDispaly則設定if(node.oldDisplay) node.style.display = node.oldDisplay;//沒有則設定為元素預設值或元素當前值else node.style.display = getDisplay(node);}})//鏈式調用return this;},hide: function(){Array.prototype.forEach.call(this, function(node){if(!isHidden(node)) {//jQuery使用其cache機制儲存資訊,這裡簡化一下//直接掛載在對應的dom下node.oldDisplay = getDisplay(node);node.style.display = 'none';}})return this;}}function getDisplay(node){var display = window.getComputedStyle(node, null).getPropertyValue('display');if(display === 'none'){var dom = document.createElement(node.nodeName);//插入到body中document.body.appendChild(dom);//即可擷取到元素display的預設值var display = window.getComputedStyle(dom, null).getPropertyValue('display');document.body.removeChild(dom);}return display;}function isHidden(node) {//忽略未append進document的元素這種隱藏情況:$('<div>block</div>')未appendreturn window.getComputedStyle(node, null).getPropertyValue('display') === 'none';}jQuery.prototype.init.prototype = jQuery.prototype;window.$ = jQuery;})(window);
先拿hide函數熱身一下。如上篇提到的,jQuery會將擷取到的nodeList處理成數組,所以一上來,我們用forEach處理數組裡的每一個node節點。
接下來,我們只需要將每一個節點的style.display置為'none'即可隱藏。很簡單,對吧?(⊙0⊙) 。oldDisplay和return this先不管╰( ̄▽ ̄)╮
hide: function(){Array.prototype.forEach.call(this, function(node){if(!isHidden(node)) {//jQuery使用其cache機制儲存資訊,這裡簡化一下//直接掛載在對應的dom下node.oldDisplay = getDisplay(node);node.style.display = 'none';}})return this;}
其中isHidden是判斷該元素是否隱藏:已經隱藏的元素就沒必要再去處理了,直接跳過
function isHidden(node) {//忽略未append進document的元素這種隱藏情況:$('<div>block</div>')未appendreturn window.getComputedStyle(node, null).getPropertyValue('display') === 'none';}
--------------------------
接下來,來個稍繁瑣的show。先拋出一個問題來引發一系列問題:
hide某個元素只需要將display:none,那麼show呢?
display:block不就行了嗎?這樣確實可以將元素顯示出來。但是萬一元素原來的值是display:inline呢?
那在hide處儲存原來的值不就行了嗎?就像以下的代碼:
node.oldDisplay = getDisplay(node);
要是執行show前沒有不執行hide呢?比如下面這種情況,不就沒有oldDisplay了嗎(⊙0⊙)
<style>div{ display:none; }</style><div>display:none</div>$('div').show()
好,關鍵的地方到了:我們擷取元素display的預設值就可以了吧?比如div預設是block,span預設是inline。
思路有了,那麼接下來的問題是:如何擷取元素display的預設值?
嘿嘿嘿,想不到吧?這裡需要用點小技巧,大體思路如下:通過nodeName建立一個新的標籤,再擷取。
有個地方可以再最佳化一下,getDisplay擷取到元素display預設值後,可以使用jQuery的cache機制存起來(實際上jQuery也是這麼做了)。
function getDisplay(node){var display = window.getComputedStyle(node, null).getPropertyValue('display');if(display === 'none'){var dom = document.createElement(node.nodeName);//插入到body中document.body.appendChild(dom);//即可擷取到元素display的預設值var display = window.getComputedStyle(dom, null).getPropertyValue('display');document.body.removeChild(dom);}return display;}
然後,綜合這兩種情況:
//有oldDispaly則設定if(node.oldDisplay) node.style.display = node.oldDisplay;//沒有則設定為元素預設值或元素當前值else node.style.display = getDisplay(node);
以為這樣就結束了?NO,show函數的情況還是挺複雜的,我們大致要應對這幾種情況:
<style>#none,#none2{ display: none; }</style><body><div id="div">預設值為block</div><span id="span">預設值為inline</span><div id="div2" style="display:inline-block;">修改為inline-block</div><div id="none">通過css隱藏了</div><div id="none2" style="display:none">通過css和style隱藏了</div></body>
最終,show函數變成了這鬼樣ψ(╰_╯)。大致思路如下:
show: function(){Array.prototype.forEach.call(this, function(node){//if(node.style) continue; //textnode沒有style//刪除style上的display:nonevar display = node.style.display;if(display === 'none'){//dispaly置為空白後,css如果有display則css的生效//否則預設的生效node.style.display = '';}//元素display值為非預設值情況,需要還原為oldDisplay:div->display:inline-block//或 檢測css上的display是否為noneif(node.style.display==='' || isHidden(node)){//有oldDispaly則設定if(node.oldDisplay) node.style.display = node.oldDisplay;//沒有則設定為元素預設值或當前值else node.style.display = getDisplay(node);}})}
--------------------------
鏈式調用就是類似這種情況:
$('div').show().hide().css('height','300px').toggle()
實現起來非常簡單,只要在每個函數後面return this即可
--------------------------
有同學說:喂!這個show,hide不對吧?是不是漏了時間參數? 用setTimeOut自己實現吧~>_<~+。
本節最主要是讓大家知道jQuery需要考慮的情況非常多(很多髒活)。即時簡化了代碼,依然還是這麼長。
寫完後,發現show還有一種情況沒考慮:
div{ display:none !important; }<div>大家自己開腦洞,怎麼處理吧(⊙0⊙)</div>