起步
我们基本都是通过jq的 .css()
来设置样式的。原生的js是这样设置的:
var head= document.getElementById("head");
head.style.width = "20px";
head.style.height = "10px";
head.style.display = "block";
总的来说,单一的设置会导致浏览器绘制一次,而且,jstyle只是针对行类样式,对于 link
引入的样式无法获取。样式属性名的兼容问题,比如驼峰,保留字 float
。
样式规则
任何支持 style
特性的 HTML
元素在 js 中有一个对象的 style
属性,其实也是一个实例,但是内部属性命名都是采用的驼峰形式的,比如 background-image
要写成 backgroundImage
,其中一个是比较特殊的就是 float
,保留字嘛所以就换成 cssFloat
,IE : styleFloat,然后对于width、hight
这些处理都最好要有一个量度单位。
合并cssText
可能针对一种情况给出的处理就是 cssText 合并。
var head= document.getElementById("head");
head.style.cssText="width:20px;height:10px;display:bolck";
和 innerHTML
一样,cssText
很快捷且所有浏览器都支持。此外当批量操作样式时,cssText 只需一次 reflow
,提高了页面渲染性能。当然如果是在创建的时候,我们还可以利用文档碎片,缺点自然就是样式被整体覆盖了,所以在处理的时候应该要先获取需要保留的样式然后再拼接起来。
样式访问
ele.style
只能获取行类样式了,那么CSS外部样式表定义的样式如何处理?
DOM2 规范增加了 defauleView 接口,提供了 getComputedStyle()
方法可以返回了类似 style 属性的 css
的属性合集。
getComputedStyle与style的区别: 区别就在于 getComputedStyle
是只能读的, style
是可以可读可写的。
jq中:
var getStyles = function( elem ) {
// Support: IE<=11+, Firefox<=30+ (#15098, #14150)
// IE throws on elements created in popups
// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
if ( elem.ownerDocument.defaultView.opener ) {
return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
}
return window.getComputedStyle( elem, null );
};
elem.ownerDocument.defaultView.getComputedStyle()
如果没有defauleView前缀,查了下在浏览器中,该属性返回当前 document 对象所关联的 window 对象,如果没有,会返回 null,应该就能直接window调用了。所以这句话就相当于window.getComputedStyle()
.getComputedStyle
方法IE6~8是不支持的。
以上就是样式操作需要了解的基础,那么总的来说 jQuery 需要的处理问题就显而易见了。
- 参数传递
- 命名规范
- 访问规则
- 性能优化
$.css()
这是$.css()
静态方法,不是jq对象方法.css()
。.css()
会处理一些其他的再调用静态方法,先看看静态方法:
// 默认computedStyle/currentStyle方式只读,也可styles指定读取对象
css: function( elem, name, extra, styles ) {
var val, num, hooks,
origName = jQuery.camelCase( name );
// Make sure that we're working with the right name
//name修正,属性兼容(同style)
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
// Try prefixed name followed by the unprefixed name
// 钩子
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// If a hook was provided get the computed value from there
// 若有钩子,通过钩子读取
if ( hooks && "get" in hooks ) {
val = hooks.get( elem, true, extra );
}
// Otherwise, if a way to get the computed value exists, use that
// 没有钩子,通过封装的curCSS读取
if ( val === undefined ) {
val = curCSS( elem, name, styles );
}
// Convert "normal" to computed value
// 属性值为"normal",若为cssNormalTransform内的属性,把对应值输出
if ( val === "normal" && name in cssNormalTransform ) {
val = cssNormalTransform[ name ];
}
// Make numeric if forced or a qualifier was provided and val looks numeric
// extra === "" 去单位处理,若为"normal"、"auto"等字符串,原样返回
// extra === true 强制去单位,若为parseFloat后为NaN的字符串,返回0
// extra === false/undefined 不特殊处理
if ( extra === "" || extra ) {
num = parseFloat( val );
return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
}
return val;
}
首先是检测是是否驼峰写法了,如果不是就得转化下:
origName = jQuery.camelCase( name );
正则带入后:
"background-image".replace( /-([\da-z])/gi, function( all, letter ) {
return letter.toUpperCase();
});
找到"-"
后面的,转成大写开头。
修正命名 float
:
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
cssProps里面其实:
cssProps: {
"float": "cssFloat"
},
$.cssHooks
有以下钩子:
这就表示,如果是其他设置项,就会通过 curCSS
处理的取值,这是是默认的动作:
function curCSS( elem, name, computed ) {
var width, minWidth, maxWidth, ret,
style = elem.style;
computed = computed || getStyles( elem );
// Support: IE9
// getPropertyValue is only needed for .css('filter') (#12537)
// getComputedStyle(elem).getPropertyValue(name)其实也可以用来获取属性,但是不支持驼峰,必须-连接书写,否则返回""
if ( computed ) {
ret = computed.getPropertyValue( name ) || computed[ name ];
}
if ( computed ) {
if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
ret = jQuery.style( elem, name );
}
// Support: iOS < 6
// A tribute to the "awesome hack by Dean Edwards"
// iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
// 为了兼容有的浏览器margin相关方法返回百分比等非px值的情况,由于width输出是px,并且margin的百分比是按照width计算的
// 因此可以直接赋值width。设置minWidth/maxWidth是为了保证设置的width不会因为超出限制失效
// Remember the original values
// 记忆
width = style.width;
minWidth = style.minWidth;
maxWidth = style.maxWidth;
// Put in the new values to get a computed value out
// 把margin的值设置到width,并获取对应width值作为结果
style.minWidth = style.maxWidth = style.width = ret;
ret = computed.width;
// Revert the changed values
// 还原
style.width = width;
style.minWidth = minWidth;
style.maxWidth = maxWidth;
}
}
// 都以字符串返回
return ret !== undefined ?
// Support: IE
// IE returns zIndex value as an integer.
ret + "" :
ret;
}
默认使用getStyles,也可通过computed参数指定样式对象。内部还有对文档片段和margin类属性值的特殊处理
$.style()
处理的方式与css类似,只是要注意了style才具有样式的修改权限
这样的传对象其实都是需要调用多次style处理的,当然没有采用cssText的方式处理,因为本身以前的属性就会丢失了
var color = a.css({"background-color":'red','width':'200px'});
.css的实现:
jQuery.fn.extend({
css: function( name, value ) {
return access( this, function( elem, name, value ) {
var styles, len,
map = {},
i = 0;
if ( jQuery.isArray( name ) ) {
styles = getStyles( elem );
len = name.length;
for ( ; i < len; i++ ) {
map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
}
return map;
}
return value !== undefined ?
jQuery.style( elem, name, value ) :
jQuery.css( elem, name );
}, name, value, arguments.length > 1 );
},
});
当时设值操作的时候,jQuery.style( elem, name, value )
起作用:
// elem.style方式读写
style: function( elem, name, value, extra ) {
// elem为文本和注释节点直接返回
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
return;
}
var ret, type, hooks,
origName = jQuery.camelCase( name ),//name修正,属性兼容
style = elem.style;
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
// 先看看有没有钩子 name、后origName使钩子更灵活,既可统一,又可单独
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// Check if we're setting a value
// elem.style方式 - 赋值
if ( value !== undefined ) {
type = typeof value;
// Convert "+=" or "-=" to relative numbers (#7345)
// '+='、'-='增量运算
if ( type === "string" && (ret = rrelNum.exec( value )) ) {
// adjustCSS对初始值和value进行单位换算,相加/减得到最终值(数值)
value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
// Fixes bug #9237
// 数值需要在下面加上合适的单位
type = "number";
}
// Make sure that null and NaN values aren't set (#7116)
if ( value == null || value !== value ) {
return;
}
// If a number, add 'px' to the (except for certain CSS properties)
// 数值和'+=xx'转换的数值,都需要加上单位。cssNumber记录了可以是数字的属性,否则默认px
if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
value += "px";
}
// Support: IE9-11+
// background-* props affect original clone's values
if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
style[ name ] = "inherit";
}
// If a hook was provided, use that value, otherwise just set the specified value
// 有钩子先使用钩子,看返回值是否为undefined决定是否style[ name ]赋值,否则直接赋值
if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
style[ name ] = value;
}
} else { // elem.style方式 - 取值
// If a hook was provided get the non-computed value from there
// 有钩子先使用钩子,看返回值是否为undefined决定是否style[ name ]取值,否则直接取值
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 ];
}
},
jQuery.style()的处理流程:
- 分解参数
- 转换为驼峰式,修正属性名
- 如果有钩子,则调用钩子的set get
- 最终实现都是依靠浏览器自己的API的
.addClass
dom就提供 elem.className
来获取class值,经过字符串拼接处理,最后elem.className = finalValue;
就可以完成class的添加了,代码就不贴了。