zoukankan      html  css  js  c++  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没有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>
  • 相关阅读:
    【HTML5 绘图与动画】使用canvas
    【H5新增元素和文档结构】新的全局属性 1. contentEditable 可编辑内容 2. contextmenu 快捷菜单 3. data 自定义属性 4. draggable 可拖动 5. dropzone 拖动数据 6. hidden 隐藏 7. spellcheck 语法检查 8. translate 可翻译
    【H5新增元素和文档结构】完善旧元素 1. a 超链接 2. ol 有序列表 3. dl 定义列表 4. cite 引用文本 5. small 小号字体 6. iframe 浮动框架 7. script 脚本
    【H5新增元素和文档结构】新的语义信息 1. address 2. time 3. figure 跟 figcaption 4. details 和 summary 5. mark 6. progress 7. meter 8. dialog 9.bdi 10. wbr 11. ruby、rt、rp 12. command
    【H5新增元素跟文档结构】新的文档结构 1. article 文章块 2. section 区块 3. nav 导航条 4. aside 辅助栏 5. main 主要区域 6. header 标题栏 7. hgroup 标题组 8. footer 页脚栏
    5_PHP数组_3_数组处理函数及其应用_9_数组集合运算函数
    【华为云技术分享】鲲鹏弹性云服务器GCC交叉编译环境搭建指南
    【华为云技术分享】7 分钟全面了解位运算
    【华为云技术分享】Linux内核编程环境 (1)
    【华为云技术分享】华为云MySQL 8.0正式商用,全新增强版开源利器强势来袭
  • 原文地址:https://www.cnblogs.com/jiahuix/p/5478475.html
Copyright © 2011-2022 走看看