jQuery 源码分析 CSS 操作原理

jquery.fn.css获取当前jQuery所匹配的元素中第一个元素的属性值【$(…).css(cssName),注意这个cssName可以是数组】或给当前jQuery所匹配的每个元素设置样式值【$(…).css(cssname,value) / $(…).css(obj)】;

可以看见函数内部直接调用了jquery.access来处理。access将当前多个元素组成的jQuery对象所匹配的元素分解成单一元素逐个调用第二个参数中的回调function( elem, name, value );如果参数name是对象的话,access内部分解name递归调用逐个处理name的每一个key/value键值对

源码

jQuery.fn.css: function( name, value ) {
    //access将当前jQuery对象分解成单一元素逐个调用第二个参数中的回调function( elem, name, value ),
    //如果参数name是对象的话,access内部分解name递归调用逐个处理name的每一个key/value键值对
    return jQuery.access( this, function( elem, name, value ) {
        var len, styles,
        map = {},
        i = 0;
        //如果css特征名称是一个数组,比如['left','marginRight']
        if ( jQuery.isArray( name ) ) {
            styles = getStyles( elem );
            len = name.length;
            //通过$.css()获取对应的css特征值
            for ( ; i < len; i++ ) {
                map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
            }
            return map;
        }
        //value有值则调用$.style设置单个css值,value参数无值则通过$.css()获取对应的css特征值
        return value !== undefined ?
        jQuery.style( elem, name, value ) :
        jQuery.css( elem, name );
    }, name, value, arguments.length > 1 );
}

这个api比较简单,但是仔细分析里面所调用的函数会发现一大堆的知识。后面一一来发掘。

首先我们看到getStyles(elem)这个函数,看一看他的定义

if ( window.getComputedStyle ) { 
    getStyles = function( elem ) {
        return window.getComputedStyle( elem, null );
    };
    ...
//ie8-兼容
} else if ( document.documentElement.currentStyle ) {
    getStyles = function( elem ) {
        return elem.currentStyle;
    };
    ...
}

两个不同的方法window.getComputedStyle和elem.currentStyle。接下来一一分析他们。

a. window.getComputedStyle

完整的表达式window.getComputedStyle(elem,pseudo)

elem: DOM节点,必须

pseudo: 伪类,且只能是伪元素的伪类,比如::after,::before,::first-letter,::first-line。可选【Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) 之前,第二个参数“伪类”是必需的】

说明:这个函数取出来的是CSS计算后的最终使用的CSS属性值【其中不包括位置关系left/right/top/bottom的属性值,比如left:10%,那么通过getComputedStyle获取的left还是"10%"】(Safari 5.1.7的margin-right返回的是百分比,这个需要特殊处理)。只读。在现代浏览器中支持良好,不支持IE8-

举例说明(这个例子会贯穿全文):

<style>
#myList{
height: 100px;
}
#myList:after{
content: 'a';
height: 100px;
}
</style>

<ul id="myList" style='width:50%;left:666px;'><li>test</li></ul>
<ul id="myList2" style='width:50%;left:111px;'><li>test2</li></ul>

getComputedStyle的值是计算后的值(比如百分比会转换成像素),实例如下


宽度50%被转换成像素了。

getComputedStyle中第二个参数只有是伪元素的伪类才起作用,实例如下


如上图可知,只要后面的伪元素不对或没有传递伪元素伪类这个参数,则得到的还是第一个参数对应的元素的getComputedStyle值。上图中.left和.content属性可作证。

getComputedStyle得到的是一个只读数组对象,数组的每个元素是一个CSS样式名称,并且这个只读数组对象拥有与数组中每个元素一一对应的属性来保存CSS样式的值。我们先看一下结构
(window.getComputedStyle(document.getElementById('myList'),null))



但是由于不支持IE8-那么我们需要一个IE8能支持的方法。这就是IE自己的东东elem.currentStyle

b. elem.currentStyle

elem.currentStyle也是一个只读的对象。

elem.currentStyle与getComputedStyle的不同

1.
elem.currentStyle在IE8-中是一个纯对象,没有类数组的结构,不可以通过style[n]方式获得CSS样式名称;在IE9+中elem.currentStyle才是一个类数组对象。

2. elem.currentStyle获得的样式虽然是最终的CSS样式,但是并非计算过的样式,比如同样是先前的例子

getComputedStyle的width结果是像素值632px


而elem.currentStyle的结果是设置的百分比值50%


3. 样式名称的差异,比如对于float属性,IE8-中currentStyle是styleFloat


而firefox中getComputedStyle是cssFloat和float【建议不要使用,浏览器保留,而且也非标准方法,浏览器可以不支持】并存


chrome中getComputedStyle是显示是float,实际上通过float【建议不要使用,浏览器保留,而且也非标准方法,浏览器可以不支持】和cssFloat都可以获取到



而IE9是cssFloat和styleFloat都有。

所以jQuery获取float属性的方式是"float": jQuery.support.cssFloat ? "cssFloat" :
"styleFloat"

c. elem.style

elem.style和window.getComputedStyle(elem,pseudo)以及elem.currentStyle比较

elem.style是获取elem的内联样式。这是和window.getComputedStyle(elem,pseudo)以及elem.currentStyle相比不同的地方之一。


style结果可读可写,而getComputedStyle和currentStyle的结果只读。

style结果是没有计算的结果,这一点和currentStyle类似,getComputedStyle是计算过的结果。如下图中style.width的值是50%,而不是像素值

d. 现代浏览器获取样式表中某个样式的值--getPropertyValue

getPropertyValue(className)是现代浏览器(IE9+,firefox,chrome...)样式表的一个属性方法。所以只要是现代浏览器,上面getComputedStyle/currentStyle/style三种方式获取的样式表
style都可以使用该方法获取样式值。例如style.getPropertyValue("float")。

需要注意的是className是直接属性名称(比如"background-color")。是“float”而非“css-float”或“cssFloat”。


如果是使用属性获取的方式获取float值,则需要转换成"cssFloat"或"styleFloat",比如


这个比较折腾。

而由于IE8-又不支持该方法,IE8-直接使用属性获取方式


IE8-的currentStyle还支持另一种获取属性方法:style.getAttribute(className)。

需要注意的是className是可以是直接属性名称或是驼峰写法(比如"background-color"或“backgroundColor"都可以)。


所以,我们为了兼容的话,有两种处理样式的方法

1.
结合getPropertyValue(className)和getAttribute(className)使用,因为他们两className都可以是直接属性名称

2.
使用属性获取方式style[className]但是需要注意的是属性名称需要兼容,比如:"float"需要替换成"cssFloat"或"styleFloat"。

而jQuery的处理就是选择第二种方式。

说道这里,虽然jQuery使用第二种方式,但是有一个属性使用属性方式获取会失败,这就是奇葩的"filter"属性。这个属性必须使用getPropertyValue才能获取正确

用表格总结一下CSS属性获取的相关用法

特点 getComputedStyle currentStyle style
浏览器支持情况 IE9+,chrome,firefox... IE ALL
可读可写 只读 只读 可读可写
是否为计算最终值(比如百分比、比例都计算为真实的像素值)
(外部样式表+内部样式表+内联样式)的最终结果 否(只是内联样式)
属性获取方式style.name和style[name] 支持("filter"属性除外) 支持 支持
支持的获取CSS属性值的方法 getPropertyValue IE8-只支持getAttribute;IE9+支持getPropertyValue和getAttribute("filter") IE8-只支持getAttribute;IE9+支持getPropertyValue和getAttribute("filter");其他浏览器只支持getPropertyValue
突出优点 标准化,支持伪元素(如::after),获取的是计算后的结果 可读可写

接下来继续分析源码:

既然了解了各种获取CSS样式表的方法,接下来看一个获取指定的CSS样式名称对应的计算值得函数curCSS = function( elem, name, _computed ) 。难点在于通过currentStyle和getComputedStyle获取到的值可能是百分比或相对值得时候,我们需要进行模拟计算出真实的值。过程比较点单,看源码注释

//备注:我们在window.getComputedStyle包含了"window"
//因为node.js中的js DOM如果没有他的话将被终止
if ( window.getComputedStyle ) { 
    getStyles = function( elem ) {
        return window.getComputedStyle( elem, null );
    };
  curCSS = function( elem, name, _computed ) {
    var width, minWidth, maxWidth,
    computed = _computed || getStyles( elem ),
    // getPropertyValue只有在IE9的 .css('filter')中用到
    ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
    style = elem.style;
    if ( computed ) {
      if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
        ret = jQuery.style( elem, name );
      }
      // Chrome < 17 and Safari 5.0使用"计算结果"替代"使用的值"来计算margin-right
      // Safari 5.1.7 (最新)返回百分比但是我们需要像素值,这一点违背CSSOM草案
      //http://dev.w3.org/csswg/cssom/#resolved-values
      //模拟计算
      if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
        //保存原值
        width = style.width;
        minWidth = style.minWidth;
        maxWidth = style.maxWidth;
        //放入新的值来获得计算值,比如marginRight为10%的时候,通过放入width为10%,然后通过computed.width即可获得10%对应的px宽度
        style.minWidth = style.maxWidth = style.width = ret;
        ret = computed.width;
        //还原更改的值
        style.width = width;
        style.minWidth = minWidth;
        style.maxWidth = maxWidth;
      }
    }
    return ret;
  };
//低版本ie兼容
} else if ( document.documentElement.currentStyle ) {
    getStyles = function( elem ) {
        return elem.currentStyle;
    };
    curCSS = function( elem, name, _computed ) {
        var left, rs, rsLeft,
        computed = _computed || getStyles( elem ),
        ret = computed ? computed[ name ] : undefined,
        style = elem.style;
        //这里避免给ret设置空字符
        //所以我们不默认为”auto”
        if ( ret == null && style && style[ name ] ) {
            ret = style[ name ];
        }
        
        // 我们对以奇怪结尾的数字(比如1em)要把他转化成像素
        // 但是不能是位置属性,应为他们是和父元素成比例的,并且我们不能测量父元素的比例,因为他可能是一大堆比例堆叠而成(比如<div style=’left:10%’><p style=’left:20%’><span style=’left:20%’></span></p></div>),根本无法计算
        if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
            //保存原值
            left = style.left;
            rs = elem.runtimeStyle;
            rsLeft = rs && rs.left;
            //放入新的值来获得计算值
            if ( rsLeft ) {
                rs.left = elem.currentStyle.left;
            }
            style.left = name === "fontSize" ? "1em" : ret;
            ret = style.pixelLeft + "px";
            //还原更改的值
            style.left = left;
            if ( rsLeft ) {
                rs.left = rsLeft;
            }
        }
        return ret === "" ? "auto" : ret;
    };
}

根据jQuery.fn.css函数最后的处理

return value !== undefined ?
        jQuery.style( elem, name, value ) :
        jQuery.css( elem, name );

可知css处理的两个关键低级api:jQuery.style和jQuery.css。

前面已经分析了只有.style是可读可写的,同理,这里的jQuery.style作用也是用来读写内联样式的。jQuery.style的处理流程为

1.修正css特征名称保存为origName,真正能被浏览器识别的名称保存为name。
2.查找name或origName的cssHooks。
3.如果是设置值(有传递value参数),则设置之。其中比较特殊的处理是value可以是累计字符串(如:“+=”)需要先通过jQuery.css取出原来的值来计算。如果value是数字,需要根据情况看是否添加“px”单位。如果是有相应的cssHooks也要特殊处理等等。
4.如果是获取值(没有传递value参数),分两种情况,有hooks通过hooks获取,否则直接style[name]即可。

源码

//给DOM节点设置或获取样式特征值
jQuery.style: function( elem, name, value, extra ) {
    //文本和注释节点不能设置样式特征值
    if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
        return;
    }
    //修正css特征名称以适配当前浏览器
    var ret, type, hooks,
        origName = jQuery.camelCase( name ),
        style = elem.style;
    //jQuery.cssProps缓存查询过的css特征名称供后续便捷查找使用
    name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
    //获取有前缀版本的hooks或没有前缀版本的hooks
    hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
    //如果是给css特征名称设置值
    if ( value !== undefined ) {
        type = typeof value;
        // convert relative number strings (+= or -=) to relative numbers. #7345
        //rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" )
        //转换相对数字符串+=/-=为相应的数字
        if ( type === "string" && (ret = rrelNum.exec( value )) ) {
            //(+ + 1) == 1;(- + 1) == -1
            value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
            // Fixes bug #9237
            type = "number";
        }
        //NaN和null不可用
        if ( value == null || type === "number" && isNaN( value ) ) {
            return;
        }
        //除开不可设置为像素单位的css特征,其他的都添加上“px”
        if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
            value += "px";
        }
        //support.clearCloneStyle = div.style.backgroundClip === "content-box";
        //div.style.backgroundClip为非“content-box”模式且
        //设置的值为空的background...将其设置为继承父节点样式
        // Fixes #8908, 更准确的做法是对每一个问题特征都设置默认值,但是这至少会调用8次函数
        if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
            style[ name ] = "inherit";
        }
        //如果提供了hook则使用hook设置值,否则设置置顶的值
        if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
            //当要设置的值为无效值的时候ie会抛出异常
            // Fixes bug #5509
            try {
                style[ name ] = value;
            } catch(e) {}
        }
    //获取值
    } else {
        //如果提供了hook则使用hook取值
        if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
            return ret;
        }
        // 其他情况从style对象中取值
        return style[ name ];
    }
}
//返回一个css特征名称,该名称可能是供应商添加了前缀的特征名
function vendorPropName( style, name ) {
    //短名称未添加厂商前缀
    if ( name in style ) {return name; }
    //检查供应商的前缀名
    //cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]
    var capName = name.charAt(0).toUpperCase() + name.slice(1),
        origName = name,
        i = cssPrefixes.length;
    while ( i-- ) {
        name = cssPrefixes[ i ] + capName;
        if ( name in style ) {return name; }
    }
    return origName;
}

jQuery.css处理也比较简单

1.修正css特征名称保存为origName,真正能被浏览器识别的名称保存为name
2.如果存在相应的cssHooks,则处理之;否则使用curCSS方法获取样式值
3.对获取到的样式值做一些默认值得处理,比如css样式fontWeight的默认值为“normal”对应的值应该是400。

源码

//获取name对应的css特征值
css: function( elem, name, extra, styles ) {
    var num, val, hooks,
    origName = jQuery.camelCase( name );
    //修正名称
    name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
    //获取有前缀版本的hooks或没有前缀版本的hooks
    hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
    //从hook中提取值
    if ( hooks && "get" in hooks ) {
        val = hooks.get( elem, true, extra );
    }
    //其他情况使用curCSS取值
    if ( val === undefined ) {
        val = curCSS( elem, name, styles );
    }
    //cssNormalTransform = {letterSpacing: 0,fontWeight: 400}
    //将"normal"转化成计算值
    if ( val === "normal" && name in cssNormalTransform ) {
        val = cssNormalTransform[ name ];
    }
    //当强制或提供了限定和val看上去像数字时强制转化成数字并返回
    if ( extra === "" || extra ) {
        num = parseFloat( val );
        return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
    }
    return val;
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索css
, 数组
, 对象
, 浏览器
, 参数
属性
jquery css 源码、jquery源码实现原理、jquery操作css、jquery操作css样式、jquery操作多个css,以便于您获取更多的相关知识。

时间: 2024-08-03 21:48:06

jQuery 源码分析 CSS 操作原理的相关文章

jQuery源码分析-03构造jQuery对象-工具函数_jquery

作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 读读写写,不对的地方请告诉我,多多交流共同进步,本章的的PDF等本章写完了发布. jQuery源码分析系列的目录请查看 http://nuysoft.iteye.com/blog/1177451,想系统的好好写写,目前还是从我感兴趣的部分开始,如果大家有对哪个模块感兴趣的,建议优先分析的,可以告诉我,一起学习. 3.4 其他静态工具函数

jQuery源码分析之jQuery.fn.each与jQuery.each用法_jquery

本文实例讲述了jQuery源码分析之jQuery.fn.each与jQuery.each用法.分享给大家供大家参考.具体分析如下: 先上例子,下面代码的作用是:对每个选中的div元素,都给它们添加一个red类 复制代码 代码如下: $('div').each(function(index, elem){       $(this).addClass('red'); } }); 上面用的的.each,即jQuery.fn.each,其内部是通过jQuery.each实现的 复制代码 代码如下: j

Jquery源码分析---导言

jQuery是一个非常优秀的JS库,与Prototype,YUI,Mootools等众多的Js类库 相比,它剑走偏锋,从web开发的实用角度出发,抛除了其它Lib中一些中看但不 实用的东西,为开发者提供了优美短小而精悍的类库.其使用简单,文档丰富, 而且性能高效,能极大地提高web系统的开发效率.因此可以说是web应用开发中 最佳的Js辅助类库之一.大部分开发者正在抛弃Prototype,而选择Jquery做为 他们进行web开发的JS库. 如是开发人员仅仅只知道文档中的简单的使用 方法,却不明

jQuery源码分析-01总体架构分析_jquery

1. 总体架构 1.1 自调用匿名函数 self-invoking anonymous function 打开jQuery源码,首先你会看到这样的代码结构: 复制代码 代码如下: (function( window, undefined ) { // jquery code })(window); 1. 这是一个自调用匿名函数.什么东东呢?在第一个括号内,创建一个匿名函数:第二个括号,立即执行 2. 为什么要创建这样一个"自调用匿名函数"呢? 通过定义一个匿名函数,创建了一个"

Jquery源码分析---概述

jQuery是一个非常优秀的JS库,与Prototype,YUI,Mootools等众多的Js类库 相比,它剑走偏锋,从web开发实用的角度出发,抛除了其它Lib中一些不实用的 东西,为开发者提供了短小精悍的类库.其短小精悍,使用简单方便,性能高效 ,能极大地提高开发效率,是开发web应用的最佳的辅助工具之一.因此大部分 开发者在抛弃Prototype而选择Jquery来进行web开发. 一些开发人员在使用jquery时,由于仅仅只知道Jquery文档中的使用方法, 不明白Jquery的运行原理

jQuery源码分析之jQuery中的循环技巧详解_jquery

jQuery的源码中有很多值得学习借鉴的技巧,本文即收集了jQuery中出现的各种遍历技巧和场景.具体分析如下: // 简单的for-in(事件) for ( type in events ) { } // 缓存length属性,避免每次都去查找length属性,稍微提升遍历速度 // 但是如果遍历HTMLCollection时,性能提升非常明显,因为每次访问HTMLCollection的属性,HTMLCollection都会内部匹配一次所有的节点 for ( var j = 0, l = ha

jQuery源码分析之Callbacks详解

 这篇文章主要分为以下知识:什么是Callbacks.Callbacks模型.基本模块实现.once和auto(memory).源码和源码下载,十分的细致全面,这里推荐给大家,有需要的小伙伴参考下吧.     代码的本质突出顺序.有序这一概念,尤其在javascript--毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是需要有序的执行.优秀代码常常 把函数切割成各自的模块,然后在某一特定条件下执行,既然这些函

Jquery源码分析---expand(扩展)

9 jquery的架构 9.1.架构概述 前面二篇就jquery 的源码进 行了逐一进行了分析.那么我们现在站在一定的高度来分析或看看jquery的源码 . Jquery的源码不同于prototype,mootools,它们对 Array,String,Event,Hash都进行了大量的扩展,然后才对element,form之类的 dom元素提供了方便及兼容的操作. Jqueryr 源码也不同于YUI,采用组 件的方式按照java面向对象的中规中律地去构建类库,向用户提供方便地操作. Jquer

Jquery源码分析---jQuery类数组的分析

4.1.类数组构建 从上节可以看出jquery构建函数完成了查找或转换 或其它的功能,其结果就是查找到元素.Dom树查找,html转换成Dom元素,还是 直接传入Dom元素都不过是方式而已.找到这些元素就得找个地方去存储起来. 存储有序数据的地方(集合)在JS中最好的当然是数组.那么又如何在 jQuery内面实现数组呢?可以采用如下的方式: jQuery.fn.prototype=new Array(); 在上一节中 的this.setArray(arr)函数中加上 Array.apply (t