• 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没有style
    24                 
    25                 //删除style上的display:none
    26                 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-block
    34                 //或 检测css上的display是否为none
    35                 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>')未append
    77         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没有style
    24         
    25         //删除style上的display:none
    26         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-block
    34         //或 检测css上的display是否为none
    35         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>
  • 相关阅读:
    新新人加入博客园
    C#通过第三方组件生成二维码(QR Code)和条形码(Bar Code)
    关于delphi 类的属性定义property方法
    从XML文件乱码问题,探寻其背后的原理
    Clang RecursiveASTVisitor & ASTFrontendActions based on it
    Clang FrontendActions
    C++ 学习笔记
    Clang Preprocessor 类的创建
    世上最伟大的十个公式
    RestEasy+用户指南第5章.@PathParam
  • 原文地址:https://www.cnblogs.com/jiahuix/p/5478475.html
Copyright © 2020-2023  润新知