你的位置:首页 > Java教程

[Java教程]jQuery的XX如何实现?——2.show与链式调用


往期回顾:

jQuery的XX如何实现?——1.框架

--------------------------

源码链接:内附实例代码

jQuery使用许久了,但是有一些API的实现实在想不通。于是抽空看了jQuery源码,现在把学习过程中发现的一些彩蛋介绍给大家(⊙0⊙)。

下面将使用简化的代码来介绍,主要关注jQuery的实现思想~>_<~

相较于上一篇,代码更新了:21~78

 1 (function(window, undefined){ 2  3   function jQuery(sel){ 4     return new jQuery.prototype.init(sel); 5   } 6    7   jQuery.prototype = { 8     constructor: jQuery, 9     init: function(sel){10       if(typeof sel === 'string'){11         var that = this;12         var nodeList = document.querySelectorAll(sel);13         Array.prototype.forEach.call(nodeList, function(val, i){14           that[i] = val;15         })16         this.selector = sel;17         this.length = nodeList.length;18       }19     },20     21     show: function(){22       Array.prototype.forEach.call(this, function(node){23         //if(node.style) continue; //textnode没有style24         25         //删除style上的display:none26         var display = node.style.display;27         if(display === 'none'){28           //dispaly置为空后,css如果有display则css的生效29           //否则默认的生效30           node.style.display = '';31         }32 33         //元素display值为非默认值情况,需要还原为oldDisplay:div->display:inline-block34         //或 检测css上的display是否为none35         if(node.style.display==='' || isHidden(node)){36           //有oldDispaly则设置37           if(node.oldDisplay) node.style.display = node.oldDisplay;38           //没有则设置为元素默认值或元素当前值39           else node.style.display = getDisplay(node);40         }41       })42       43       //链式调用44       return this;45     },46     47     hide: function(){48       Array.prototype.forEach.call(this, function(node){49         if(!isHidden(node)) {50           //jQuery使用其cache机制存储信息,这里简化一下51           //直接挂载在对应的dom下52           node.oldDisplay = getDisplay(node);53           node.style.display = 'none';54         }55       })56       57       return this;58     }59   }60   61   function getDisplay(node){62     var display = window.getComputedStyle(node, null).getPropertyValue('display');63     64     if(display === 'none'){65       var dom = document.createElement(node.nodeName);66       //插入到body中67       document.body.appendChild(dom);68       //即可获取到元素display的默认值69       var display = window.getComputedStyle(dom, null).getPropertyValue('display');70       document.body.removeChild(dom);71     }72     return display;73   }74   75   function isHidden(node) {76     //忽略未append进document的元素这种隐藏情况:$('<div>block</div>')未append77     return window.getComputedStyle(node, null).getPropertyValue('display') === 'none';78   }79   80   jQuery.prototype.init.prototype = jQuery.prototype;81   82   window.$ = jQuery;83 })(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>')未append  return window.getComputedStyle(node, null).getPropertyValue('display') === 'none';}

 

 

--------------------------

接下来,来个稍繁琐的show。先抛出一个问题来引发一系列问题:

hide某个元素只需要将display:none,那么show呢?

display:block不就行了吗?这样确实可以将元素显示出来。但是万一元素原来的值是display:inline呢?

那在hide处保存原来的值不就行了吗?就像以下的代码:

52 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;}

 然后,综合这两种情况:

36 //有oldDispaly则设置37 if(node.oldDisplay) node.style.display = node.oldDisplay;38 //没有则设置为元素默认值或元素当前值39 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函数变成了这鬼样ψ(╰_╯)。大致思路如下:

21 show: function(){22   Array.prototype.forEach.call(this, function(node){23     //if(node.style) continue; //textnode没有style24     25     //删除style上的display:none26     var display = node.style.display;27     if(display === 'none'){28       //dispaly置为空后,css如果有display则css的生效29       //否则默认的生效30       node.style.display = '';31     }32 33     //元素display值为非默认值情况,需要还原为oldDisplay:div->display:inline-block34     //或 检测css上的display是否为none35     if(node.style.display==='' || isHidden(node)){36       //有oldDispaly则设置37       if(node.oldDisplay) node.style.display = node.oldDisplay;38       //没有则设置为元素默认值或当前值39       else node.style.display = getDisplay(node);40     }41   })42 }

 


--------------------------

链式调用就是类似这种情况:$('div').show().hide().css('height','300px').toggle()

实现起来非常简单,只要在每个函数后面return this即可


--------------------------

有同学说:喂!这个show,hide不对吧?是不是漏了时间参数?  用setTimeOut自己实现吧~>_<~+。

本节最主要是让大家知道jQuery需要考虑的情况非常多(很多脏活)。即时简化了代码,依然还是这么长。

 

写完后,发现show还有一种情况没考虑:

div{ display:none !important; }<div>大家自己开脑洞,怎么处理吧(⊙0⊙)</div>