zoukankan      html  css  js  c++  java
  • jQuery-1.9.1源码分析系列(七) 钩子(hooks)机制及浏览器兼容续

      前面一章分析了jQuery.support、钩子原理和属性钩子。这一章主要分析CSS钩子。

    b. css操作的钩子


      CSS钩子种类:

      cssHooks

      cssNumber

      cssProps

    jQuery.cssHooks的对象

      不过cssHooks中的set函数的作用有些不同,set函数并没有真正的设置相应的值,而是修正要设置到CSS中的值。获取到修正值以后,设置在jQuery.style函数中进行。后面分析几个CSS钩子

      获取opacity返回的值需要时数字

    cssHooks: {
      opacity: {
        get: function( elem, computed ) {
          if ( computed ) {
            //需要返回数字                      
            var ret = curCSS( elem, "opacity" );
            return ret === "" ? "1" : ret;
          }
        }
      }
    },
    View Code

      当设置display为none等的时候是不能获取到宽高的,所以需要将元素设为display为block,visibility设置为hidden来获取宽高;设置宽高需要根据CSS样式boxSizing的取值来确定。

    jQuery.each([ "height", "width" ], function( i, name ) {
      jQuery.cssHooks[ name ] = {
        get: function( elem, computed, extra ) {
          if ( computed ) {
                   //当设置display为none等的时候是不能获取到宽高的,
                    //所以需要将元素设为display为block,
                   //visibility设置为hidden来获取宽高
                   // rdisplayswap = /^(none|table(?!-c[ea]).+)/,
                   //cssShow ={ position: "absolute", visibility: "hidden", display: "block" }
     
                   return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
                    jQuery.swap( elem, cssShow, function() {
                        return getWidthOrHeight( elem, name, extra );
    
                    }) :
                    getWidthOrHeight( elem, name, extra );
               }
           },
           
           set: function( elem, value, extra ) {
          var styles = extra && getStyles( elem );
          return setPositiveNumber( elem, value, extra ?
            augmentWidthOrHeight(elem, name, extra,
              jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",styles) : 
            0
          );
        }
      };
    });
    View Code

      IE使用filters来设置不透明度;IE要设置不透明度更加复杂,需保证有布局,如果设置不透明度为1,并且没有别的filters存在,尝试移除filter属性等

    if ( !jQuery.support.opacity ) {
      jQuery.cssHooks.opacity = {
        get: function( elem, computed ) {
          // IE使用filters来设置不透明度,ropacity = /opacitys*=s*([^)]*)/
          return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
          ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
          computed ? "1" : "";
        },
        set: function( elem, value ) {
          var style = elem.style,
          currentStyle = elem.currentStyle,
          opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
          filter = currentStyle && currentStyle.filter || style.filter || "";
     
          //IE7中滤镜(filter)必须获得布局才能生效,我们用zoom这个IE私有属性让其获得布局
          style.zoom = 1;
     
          //ie处理,如果设置不透明度为1,并且没有别的filters存在,尝试移除filter属性
          //如果只为“”,删除内联的opacity,ralpha = /alpha([^)]*)/i
          if ( ( value >= 1 || value === "" ) && jQuery.trim( filter.replace( ralpha, "" ) ) === "" && style.removeAttribute ) {
            //设置style.filter为null, "" 或 " ",结果是"filter:"依然在cssText中
            //如果当下"filter:"存在则清除类型不可用,我们应当避免
            // style.removeAttribute是IE独有
            style.removeAttribute( "filter" );
     
            //如果当前没有filter样式应用于css rule或未设置内联的不透明则返回
            if ( value === "" || currentStyle && !currentStyle.filter ) {
              return;
            }
          }
     
          // 其他情况设置filter values
          style.filter = ralpha.test( filter ) ?
          filter.replace( ralpha, opacity ) :
          filter + " " + opacity;
        }
      };
    }
    View Code

      webkit的bug:getComputedStyle返回margin-right值错误;当指定为top/left/bottom/right时,使用getComputedStyle 返回百分比结果,使用jQuery( elem ).position()来获取。

    //DOM加载完成后才能做support测试,在添加下面的Hooks
    jQuery(function() {
        if ( !jQuery.support.reliableMarginRight ) {
               jQuery.cssHooks.marginRight = {
                   get: function( elem, computed ) {
                       if ( computed ) {
                           // WebKit Bug 13343 - getComputedStyle返回margin-right值错误
                           //设置元素的display为inline-block来解决
                           return jQuery.swap( elem, { "display": "inline-block" },
                                   curCSS, [ elem, "marginRight" ] );
                       }
                   }
               };
        }
     
        // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
        // getComputedStyle 返回百分比当指定为top/left/bottom/right
        //我们使用jQuery( elem ).position()来获取
        if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
               jQuery.each( [ "top", "left" ], function( i, prop ) {
                   jQuery.cssHooks[ prop ] = {
                        get: function( elem, computed ) {
                           if ( computed ) {
                                   computed = curCSS( elem, prop );
                                   // if curCSS returns percentage, fallback to offset
                                   //rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
                                   return rnumnonpx.test( computed ) ?
                                   jQuery( elem ).position()[ prop ] + "px" :
                                   computed;
                           }
                       }
                   };
               });
        }
    });
    View Code

      后面这个钩子与众不同,他使用在动画的展开上。我们知道margin/padding/borderWidth实际上不是一个CSS属性,而是四个CSS属性的集合。所以三个CSS的expand钩子实际上是将他们拆分成四个属性给提取出来。

      // 这里的hooks用在动画的展开特征上
      jQuery.each({
            margin: "",
            padding: "",
            border: "Width"
            }, function( prefix, suffix ) {
                jQuery.cssHooks[ prefix + suffix ] = {
                    expand: function( value ) {
                        var i = 0,
                        expanded = {},
    
                            //如果不是字符串则假设为一个单独的数字
                            parts = typeof value === "string" ? value.split(" ") : [ value ];
    
                            for ( ; i < 4; i++ ) {
                                //cssExpand = [ "Top", "Right", "Bottom", "Left" ],
                                expanded[ prefix + cssExpand[ i ] + suffix ] =
                                parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
                            }
    
                            return expanded;
                        }
                };
    
                if ( !rmargin.test( prefix ) ) {
                jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
            }
        });
    View Code

    jQuery. cssNumber和jQuery.cssProps的对象

    //下面的css特征值后面不能添加“px”字段
    cssNumber: {
        "columnCount": true,
        "fillOpacity": true,
        "fontWeight": true,
        "lineHeight": true,
        "opacity": true,
        "orphans": true,
        "widows": true,
        "zIndex": true,
        "zoom": true
    },
     
    //float对应的css特征名需要在使用前修正
    cssProps: {
        // normalize float css property
        "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
    },

      拓展:

      有一些CSS属性需要在某些特定条件下才能获取正确。这种情况需要模拟场景获取值,然后恢复先前的场景。swap就是用来专门模拟场景,获取值以后恢复场景的函数。

    // 一种快速切换输入/输出css特征值(计算前保存css特征,计算中更改css特征以获取计算结果,计算结束后恢复先前保存的css特征)以获取正确计算结果的方法
    swap: function( elem, options, callback, args ) {
        var ret, name,
            old = {};
     
        // 保存原来的特征值,设置为保证计算成功而修改的特征(property)值
        for ( name in options ) {
               old[ name ] = elem.style[ name ];
               elem.style[ name ] = options[ name ];
        }
        //调用回调计算结果
        ret = callback.apply( elem, args || [] );
     
        // 恢复原来的特征值
        for ( name in options ) {
               elem.style[ name ] = old[ name ];
        }
        //返回计算结果
        return ret;
    }

      setPositiveNumber函数对要设置给CSS属性的值做修正,比如添加"px"结尾等

    // rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
    //用来匹配数字,.source返回表达式字符串自身
    //core_pnum = /[+-]?(?:d*.|)d+(?:[eE][+-]?d+|)/.source,
    //返回指定value和subtract对应应该设置的css样式值
    function setPositiveNumber( elem, value, subtract ) {
        var matches = rnumsplit.exec( value );
        return matches ?
        // 注意没有定义的"subtract",例如在cssHooks中使用时
        Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
        value;
    }

      getWidthOrHeight函数提供获取CSS宽高属性的计算方法

    //获取宽度或高度
    function getWidthOrHeight( elem, name, extra ) {
        //首先获取offset特征值,相当于包括边框在内的盒宽高
        var valueIsBorderBox = true,
            val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
            styles = getStyles( elem ),
            isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
    
        //一些非html元素offsetWidth返回undefined,因此检查null/undefined
        if ( val <= 0 || val == null ) {
            //如果计算失败则在必要的情况下使用未计算的结果
            val = curCSS( elem, name, styles );
            if ( val < 0 || val == null ) {
                val = elem.style[ name ];
            }
    
            //已计算的单元为非像素单位则终止并返回
            if ( rnumnonpx.test(val) ) {
                return val;
            }
    
            //我们需要检查style,避免浏览器使用getComputedStyle返回不可靠的值而悄悄的回到可靠的elem.style值
            valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
    
            //规范“”,auto为拓展做准备
            val = parseFloat( val ) || 0;
        }
    
        //使用动态box-sizing模型来添加/减少不相干的样式
        return ( val +
            augmentWidthOrHeight(
                elem,
                name,
                extra || ( isBorderBox ? "border" : "content" ),
                valueIsBorderBox,
                styles
                )
            ) + "px";
    }
    
    //extra表示计算时要包括的部分
    function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
        //在循环中遍历cssExpand时使用
        //其中cssExpand = [ "Top", "Right", "Bottom", "Left" ],
        var i = extra === ( isBorderBox ? "border" : "content" ) ?
            //如果我们有了正确的测量结果,避免增大,正常情况下会走这一步
            4 :
            //否则初始化为水平或垂直特征(property)
            name === "width" ? 1 : 0,
    
        val = 0;
    
        for ( ; i < 4; i += 2 ) {
            //两种盒模型都排除margin,如果计算要包括margin,则加上他
            if ( extra === "margin" ) {
                val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
            }
    
            //如果是border-box模型
            if ( isBorderBox ) {
                // border-box包括padding,如果我们需要内容部分因此要减去他
                if ( extra === "content" ) {
                    val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
                }
    
                //此时,extra不是边框也非margin时,减去边框
                if ( extra !== "margin" ) {
                    val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
                }
            //如果是content-box模型
            } else {
                //此时,extra不是内容,所以加上padding
                val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
    
                //此时,extra不是内容也不是padding,所以加上边框
                if ( extra !== "padding" ) {
                    val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
                }
            }
        }
        return val;
    }
  • 相关阅读:
    爆打团队 2016.04.12 站立会议
    爆打团队 2016.04.11 站立会议
    爆打团队 2016.04.10 站立会议
    爆打小组 4.3日站立会议
    随机生成题目的代码分析
    爆打 第二周第二次站立会议(2016.3.29)
    爆打小组,四则运算,算法实现
    add some template for ec-final
    2014_acmicpc_shanghai_google
    matrix_world_final_2013
  • 原文地址:https://www.cnblogs.com/chuaWeb/p/jQuery-1-9-1-Hooks2.html
Copyright © 2011-2022 走看看