这里实际包含两个文件,在IE9中,它完全支持W3C那种精确获取样式的API,因此无需使用currentStyle与runtimeStyle,因此对于旧式IE的兼容单独放到一个JS文件中。
css.js
//=========================================
// 样式操作模块 by 司徒正美 2011.8.23
//=========================================
(function(global,DOC){
var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')],
deps = global.getComputedStyle ? "node" : "node,css_ie" ;
dom.define("css", deps, function(){
dom.log("已加载css模块")
var cssFloat = dom.support.cssFloat ? 'cssFloat': 'styleFloat',rcap = /-([a-z])/g,capfn = function($0,$1){
return $1.toUpperCase();
},
adapter = dom.cssAdapter = dom.cssAdapter || {};
function cssCache(name){
return cssCache[name] || (cssCache[name] = name == 'float' ? cssFloat : name.replace(rcap, capfn));
}
dom.mix(dom, {
//http://www.cnblogs.com/rubylouvre/archive/2011/03/28/1998223.html
cssName : function (name){
var prefixes = ['', '-ms-','-moz-', '-webkit-', '-khtml-', '-o-','ms-']
dom.cssName = function(name, target, test){
target = target || document.documentElement.style;
for (var i=0, l=prefixes.length; i < l; i++) {
test = (prefixes[i] + name).replace(rcap,capfn);
if(test in target){
return test;
}
}
return null;
}
return dom.cssName(name);
},
cssCache: cssCache,
cssNumber : dom.oneObject("fontSizeAdjust,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom"),
css: function(nodes, name, value){
var props = {}, fn
if(nodes.nodeType == 1){
nodes = [nodes]
}
if ( value === void 0 ) {//获取样式
try{
return (adapter[name+":get"] || adapter["_default:get"])( nodes[0], cssCache(name) );
}catch(e){
dom.log(e)
}
}else {//设置样式
if(typeof name == "string"){
props[name] = value;
}else{
props = name;
}
for(name in props){
value = props[name];
name = cssCache(name);
fn = adapter[name+":set"] || adapter["_default:set"];
if ( isFinite( value ) && !dom.cssNumber[ name ] ) {
value += "px";
}
for(var i = 0, node; node = nodes[i++];){
if(node && node.nodeType === 1){
fn(node, name, value );
}
}
}
}
return nodes;
}
});
dom.fn.css = function(name,value){
return dom.css(this, name,value);
}
//========================= 处理 width height =========================
var cssShow = {
position: "absolute",
visibility: "hidden",
display: "block"
}
var cssPair = {
width:['Left', 'Right'],
height:['Top', 'Bottom']
}
function swap( node, options, callback ) {
var old = {};
for ( var name in options ) {
old[ name ] = node.style[ name ];
node.style[ name ] = options[ name ];
}
callback.call( node );
for ( name in options ) {
node.style[ name ] = old[ name ];
}
}
// clientWidth = node.style.width + padding
// https://developer.mozilla.org/en/DOM/element.clientWidth
// offsetWidth = node.style.width + padding + border
// https://developer.mozilla.org/en/DOM/element.offsetWidth
// getBoundingClientRect = node.style.width + padding + border
// https://developer.mozilla.org/en/DOM/element.getBoundingClientRect
// [CSS2.1 盒子模型] http://www.w3.org/TR/CSS2/box.html
// B-------border----------+ -> border
// | |
// | P----padding----+ | -> padding
// | | | |
// | | C-content-+ | | -> content
// | | | | | |
// | | | | | |
// | | +---------+ | |
// | | | |
// | +---------------+ |
// | |
// +-----------------------+
// B = event.offsetX/Y in WebKit
// event.layerX/Y in Gecko
// P = event.offsetX/Y in IE6 ~ IE8
// C = event.offsetX/Y in Opera
function getWH( node, name ) {
var which = cssPair[name], getter = dom.cssAdapter["_default:get"],
val = name === "width" ? node.offsetWidth : node.offsetHeight;
which.forEach(function(direction){
val -= parseFloat(getter(node, 'padding' + direction)) || 0;
val -= parseFloat(getter(node, 'border' + direction + 'Width')) || 0;
});
return val;
}
"width,height".replace(dom.rword,function(name){
dom.cssAdapter[ name+":get" ] = function(node, name, value){
if ( node.offsetWidth !== 0 ) {
value = getWH( node, name ) ;
} else {
swap( node, cssShow, function() {
value = getWH( node, name );
});
}
return value + "px"
}
});
//IE9 FF等支持getComputedStyle
dom.mix(adapter, {
"_default:get" :function( node, name){
return node.style[ name ];
},
"_default:set" :function( node, name, value){
return node.style[ name ] = value;
}
},false);
if ( DOC.defaultView && DOC.defaultView.getComputedStyle ) {
adapter[ "_default:get" ] = function( node, name ) {
var ret, defaultView, computedStyle;
if ( !(defaultView = node.ownerDocument.defaultView) ) {
return undefined;
}
var underscored = name == "cssFloat" ? "float" : name.replace( /([A-Z]|^ms)/g, "-$1" ).toLowerCase();
if ( (computedStyle = defaultView.getComputedStyle( node, null )) ) {
ret = computedStyle.getPropertyValue( underscored )
ret = ret.replace(/\d*(?:\.\d+px)/,function(a){
return parseFloat(a).toFixed()+"px"
});
if ( ret === "" && !dom.contains( node.ownerDocument, node ) ) {
ret = node.style[name];//如果还没有加入DOM树,则取内联样式
}
}
return ret;
};
}
//========================= 处理 user-select =========================
//https://developer.mozilla.org/en/CSS/-moz-user-select
//http://www.w3.org/TR/2000/WD-css3-userint-20000216#user-select
//具体支持情况可见下面网址
//http://help.dottoro.com/lcrlukea.php
adapter[ "userSelect:set" ] = function( node, name, value ) {
name = dom.cssName(name);
if(typeof name === "string"){
return node.style[name] = value
}
var allow = /none/.test(value||"all");
node.unselectable = allow ? "" : "on";
node.onselectstart = allow ? "" : function(){
return false;
};
};
//http://msdn.microsoft.com/en-us/library/cc304082(v=vs.85).aspx
var test = dom('<div style="background-position: 3px 5px">');
dom.support.backgroundPosition = test.css('backgroundPosition') === "3px 5px" ? true : false;
dom.support.backgroundPositionXY = test.css('backgroundPositionX') === "3px" ? true : false;
test = null;
//如果像IE67那样,只支持backgroundPositionXY,不支持backgroundPosition
var XY = ["X","Y"],prefix = "backgroundPosition";
if (!dom.support.backgroundPosition && dom.support.backgroundPositionXY) {
adapter[prefix+":get"] = function( node, name) {
return XY.map(function(which){
return adapter[ "_default:get" ](node,prefix+which)
}).join(" ");
}
adapter[prefix+":set"] =function(node, name, value){
XY.forEach(function(which,i){
node.style[ prefix +which ] = value.split(/\s/)[i]
})
}
}
function parseBgPos(bgPos) {
var parts = bgPos.split(/\s/),
values = {
"X": parts[0],
"Y": parts[1]
};
return values;
}
if (dom.support.backgroundPosition && !dom.support.backgroundPositionXY) {
XY.forEach(function(which){
adapter[prefix+which+":get"] = function(node,name){
var values = parseBgPos( adapter[ "_default:get" ](node,prefix) );
return values[ which];
}
adapter[prefix+which+":set"] = function(node,name,value){
var values = parseBgPos( adapter[ "_default:get" ](node,prefix) ),
isX = which === "X";
node.style.backgroundPosition = (isX ? value : values[ "X" ]) + " " +
(isX ? values[ "Y" ] : value);
}
})
}
});
})(this,this.document);
css_ie.js
(function(global,DOC){
var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')];
dom.define("css_ie", function(){
dom.log("已加载css_ie模块")
var adapter = dom.cssAdapter = {};
//========================= 处理 opacity =========================
var ropacity = /opacity=([^)]*)/i, ralpha = /alpha\([^)]*\)/i,
rnumpx = /^-?\d+(?:px)?$/i, rnum = /^-?\d/;
adapter["opacity:get"] = function(node,op){
//这是最快的获取IE透明值的方式,不需要动用正则了!
if(node.filters.alpha){
op = node.filters.alpha.opacity;
}else if(node.filters["DXImageTransform.Microsoft.Alpha"]){
op = node.filters["DXImageTransform.Microsoft.Alpha"].opacity
}else{
op = (node.currentStyle.filter ||"opacity=100").match(ropacity)[1];
}
return (~~op)/100;
}
adapter["opacity:set"] = function(node, name, value){
var currentStyle = node.currentStyle, style = node.style;
if(!currentStyle.hasLayout)
style.zoom = 1;//让元素获得hasLayout
value = (value > 0.999) ? 1: (value < 0.001) ? 0 : value;
if(node.filters.alpha){
//必须已经定义过透明滤镜才能使用以下便捷方式
node.filters.alpha.opacity = value * 100;
}else{
style.filter = "alpha(opacity="+((value * 100) | 0)+")";
}
//IE7的透明滤镜当其值为100时会让文本模糊不清
if(value === 1){
style.filter = currentStyle.filter.replace(ralpha,'');
}
style.visibility = value ? "visible" : "hidden";
if(node.tagName === "TR"){//IE bug,TD与TH不会继承TR的透明度
var self = arguments.callee;
dom.slice(node.cells).forEach(function(cell){
self(cell,name, value);
});
}
}
var ie8 = !!global.XDomainRequest,
border = {
thin: ie8 ? '1px' : '2px',
medium: ie8 ? '3px' : '4px',
thick: ie8 ? '5px' : '6px'
};
adapter[ "_default:get" ] = function(node, name){
var ret = node.currentStyle && node.currentStyle[name];
if ((!rnumpx.test(ret) && rnum.test(ret))) {
// Remember the original values
var style = node.style,
left = style.left,
rsLeft = node.runtimeStyle && node.runtimeStyle.left ;
if (rsLeft) {
node.runtimeStyle.left = node.currentStyle.left;
}
style.left = name === 'fontSize' ? '1em' : (ret || 0);
ret = style.pixelLeft + "px";
// Revert the changed values
style.left = left;
if (rsLeft) {
node.runtimeStyle.left = rsLeft;
}
}
if(ret == "medium"){
name = name.replace("Width","Style");
//border width 默认值为medium,即使其为0"
if(arguments.callee(node,name) == "none"){
ret = "0px";
}
}
return ret === "" ? "auto" : border[ret] || ret;
}
});
})(this,this.document);
//2011.9.5
//将cssName改为隋性函数,修正msTransform Bug
相关测试:
dom.define("test/css","spec,css",function(){
dom.addTestModule("样式操作模块-css",{
"dom.fn.css":function(){
var el = dom('<div id="test-div" ' +
'style="padding-left: 2pt; ' +
'background: transparent; ' +
'font-size:16px;' +
'float: left; ' +
'border: 5px solid rgb(0,0,0);">css test</div>').appendTo("body");
el = dom("#test-div")
expect(el.css( 'float')).eq('left');//1
expect(el.css( 'position')).eq('static');//2
expect(el.css( 'backgroundColor')).match(function(val){
return val == "rgba(0, 0, 0, 0)" || val == "transparent"
});//3
expect(el.css( 'backgroundPosition')).eq("0% 0%");//4
expect(el.css( 'backgroundPositionX')).eq('0%');//5
expect(el.css( 'fontSize')).eq('16px');//6
expect(el.css( 'border-right-width')).eq('5px');//7
var matchFn = function(val){
val = parseFloat(val)
return val >= 2 && val <= 3
}
expect(el.css( 'paddingLeft')).match(matchFn);//8
expect(el.css( 'padding-left')).match(matchFn);//9
expect(el.css( 'padding-right')).eq('0px');//10
expect(el.css( 'opacity')).eq('1');//11
// 不加入 dom 节点,ie9,firefox 返回 auto by computedStyle
// ie7,8 返回负数,offsetHeight 返回0
//alert(elem.currentStyle.height);== auto
expect(parseInt(el.css( 'height'))).match(function(val){
val = parseFloat(val)
return val >= 18 && val <= 20
});//12
el.css( 'float', 'right');
expect(el.css( 'float')).eq('right');//13
el.css( 'font-size', '100%');
expect(el.css( 'font-size')).eq("16px");//14
el.css( 'opacity', '0.2');
expect(el.css( 'opacity')).match(function(val){
val = parseFloat(val);
return val >= 0.2 && val < 0.21;
});
el.css( 'border', '2px dashed red');
expect(el.css( 'borderLeftWidth')).eq('2px');//16
el.remove();
}
});
});
使用方法与jQuery保持一致,读写器合而为一,set all get first。
dom.require("css",function(){ var a = dom('<div style="width:100px;opacity:0.7;filter:alpha(opacity=50),blur(add=ture,direction=135,strength=200)">'); dom.log( a.css("opacity"))//IE9,FF为0.7;IE6-8为0.5 });