jquery 实现原理九:css

一,基本结构

css模块,在fn上只提供了四个方法css,show,hide和toggle。可能有人会问为什么官方文档上还有innerHeight,scrollTop,removeClass之类的方法?其实这些方法不是在css模块中提供的,只是在api中把他们都放到了css中,innerHeight是在dimensions中提供的,hasClass是在attributes/classes中提供的。
css模块提供了两个非常重要的静态方法$.css和$.style,大部分的其他函数最终都是通过调用这两个函数来实现的。
另外还有一个需要注意的是$.cssHooks,用来定义对不同属性的不同处理方式,很像c#中的getter/setter,比如height和width是要计算出来而不是直接取。
代码基本结构大概是这样的:
function vendorPropName( style, name ) { //检测浏览器前缀}
function getStyles( elem ) {//get computed style}  
function showHide( elements, show ) {//显示隐藏元素,使用data_priv来保存display}
jQuery.fn.extend({//拓展四个方法
     css: function() {}
     show: function() {}
     hide: function() {}
     toggle: function() {}   
})  

jQuery.extend({ //几个静态方法,最重要的两个静态方法css和style就是在这里定义的
     cssHooks: function() {}
     cssNumber: function() {}
     style: function() {}
     css: function() {}    
}) 

//下面是几个工具方法
curCSS = function( elem, name, _computed ) {}
function augmentWidthOrHeight() {}
function getWidthOrHeight( elem, name, extra ) {

//下面是对height和width的hook
jQuery.each([ "height", "width" ], function( i, name ) {})
…
 
二,jQuery.css和jQuery.style
这两个函数的实现都比较简单,最大的区别是:
style是计算元素自身的样式,不包括从父元素继承来的样式,而且style包含读写操作

css返回的是computedStyle,也就是包括父元素的样式,并且css只能读不能写(computedStyle是计算结果而不是一个属性,显然没法修改)

style: function( elem, name, value, extra ) {
          //style方法是读写元素的自身样式,而不计算继承来的样式
          // Don't set styles on text and comment nodes
          if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
               return;
          }

          // Make sure that we're working with the right name
          var ret, type, hooks,
               origName = jQuery.camelCase( name ),
               style = elem.style;
          //检测浏览器前缀,并且会缓存在jQuery.cssProps中
          name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );

          // gets hook for the prefixed version
          // followed by the unprefixed version
          //取hook
          hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

          // Check if we're setting a value
          if ( value !== undefined ) {
               type = typeof value;
               //下面三个if都是对数字的处理
               // convert relative number strings (+= or -=) to relative numbers. #7345
               //把字符串转成数字
               if ( type === "string" && (ret = rrelNum.exec( value )) ) {
                    value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
                    // Fixes bug #9237
                    type = "number";
               }

               // Make sure that NaN and null values aren't set. See: #7116
               if ( value == null || type === "number" && isNaN( value ) ) {
                    return;
               }

               // If a number was passed in, add 'px' to the (except for certain CSS properties)
               //如果name不在jQuery.cssNumber中,则自动追加’px’,所以,可以通过在jQuery.cssNumber中添加属性的方式来修改这一行为
               if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
                    value += "px";
               }

               // Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
               // but it would mean to define eight (for every problematic property) identical functions
               //自动把没有显示声明的属性改成’inherit'
               if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
                    style[ name ] = "inherit";
               }

               // If a hook was provided, use that value, otherwise just set the specified value
               // 如果有hook,则调用hook中的set方法来设置value
               if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
                    style[ name ] = value;
               }

          } else {
               // If a hook was provided get the non-computed value from there
               //如果有hook,则调用hook中的get方法来取value
               if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
                    return ret;
               }

               // Otherwise just get the value from the style object
               return style[ name ];
          }
     }, 

css: function( elem, name, extra, styles ) {
          // css 是读取计算后的样式
          var val, num, hooks,
               origName = jQuery.camelCase( name );

          // Make sure that we're working with the right name
          //和style中是一样的,检测浏览器前缀
          name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );

          // gets hook for the prefixed version
          // followed by the unprefixed version
          hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

          // If a hook was provided get the computed value from there
          // 使用hook.get
          if ( hooks && "get" in hooks ) {
               val = hooks.get( elem, true, extra );
          }

          // Otherwise, if a way to get the computed value exists, use that
          if ( val === undefined ) {
               // 调用curCSS,得到的是计算后的样式
               val = curCSS( elem, name, styles );
          }

          //convert "normal" to computed value
          if ( val === "normal" && name in cssNormalTransform ) {
               val = cssNormalTransform[ name ];
          }

          // Return, converting to number if forced or a qualifier was provided and val looks numeric
          if ( extra === "" || extra ) {
               num = parseFloat( val );
               return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
          }
          return val;
     }
}); 


三,jQuery.cssHooks

因为有些样式不是简单的读写属性就可以的,比如width就不是简单地读取el.style.width。为了解决这个问题,jquery定义了一个属性 $.cssHooks,这里可以自定义对某个属性的get和set操作。而且jquery中就是用cssHooks来处理某些特殊属性的,比如下面这个:

jQuery.each([ "height", "width" ], function( i, name ) {
     jQuery.cssHooks[ name ] = {
          get: function( elem, computed, extra ) {
               if ( computed ) {
                    // certain elements can have dimension info if we invisibly show them
                    // however, it must have a current display style that would benefit from this
                    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
               );
          }
     };
}); 


cssHooks很适合用来写css插件,比如你想自定义一个插件 abc,可以这样写:

 jQuery.cssHooks[ ‘abc' ] = {
     get: function(elem, computed, extra) {
          return ' I am abc'
     } 
}



最后还有一个show/hide是比较特殊的,她用到了data_priv来保存原始display,所以不会在切换的时候丢掉display属性。

你可能感兴趣的:(jquery,jquery实现原理,jquery源码解析)