CSS样式处理方法总共有三个:swap CSS curCSS
(1)、swap
swap: function(e,o,f) { for ( var i in o ) { e.style["old"+i] = e.style[i]; e.style[i] = o[i]; } f.apply( e, [] ); for ( var i in o ) e.style[i] = e.style["old"+i]; },
swap方法在css和curCSS里面有用到
虽然还不知道它在什么样的环境下使用
不过源码挺简单的
o这个形参感觉上是要接受形如
{ "width":"100px", "height":"100px", "background":"#f00" }
格式作为参数
然后先缓存了一下这些样式,再在一个DOM对象e上执行一个函数f
在DOM元素上执行的函数目前猜测应该是要改变它的某些样式
执行完这个函数之后再将缓存的样式重新赋给这个DOM对象
(2)、curCSS
curCSS: function(elem, prop, force) { var ret; if (!force && elem.style[prop]) { ret = elem.style[prop]; } else if (elem.currentStyle) { var newProp = prop.replace(/-(w)/g,function(m,c){return c.toUpperCase()}); ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; } else if (document.defaultView && document.defaultView.getComputedStyle) { prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase(); var cur = document.defaultView.getComputedStyle(elem, null); if ( cur ) ret = cur.getPropertyValue(prop); else if ( prop == 'display' ) ret = 'none'; else jQuery.swap(elem, { display: 'block' }, function() { ret = document.defaultView.getComputedStyle(this,null).getPropertyValue(prop); }); } return ret; },
先看这个简单点的方法——curCSS
通过在源码里面查找,发现第三个参数force用得很少
只有在动画模块那里传过来一个1
等分析到动画模块的源码之后再说
现在看来这个方法如果这样调用:
$.curCSS(oDiv,"height")
貌似是要获取oDiv的height值
第一个if条件从行间样式查找,如果有的话就返回
接下来的两个else if分支都是获取非行间的样式
第一个分支采用currentStyle兼容IE系列的浏览器
第二个分支采用getComputedStyle兼容标准浏览器
getComputedStyle本身是全局window对象的一个方法
因此更完整的写法应该是window.getComputedStyle(obj,null)[attr]
但是在第二个分支中貌似不是这样写的,而是写成了
document.defaultView.getComputedStyle
经过验证,绝大多数情况下window==document.defaultView返回true
但是通过查阅资料发现,据说在FireFox3 以下的版本中某些时候会返回false
jQuery为了兼容,就采取了这种做法
然后这一行:
var newProp = prop.replace(/-(w)/g,function(m,c){return c.toUpperCase()});
用来处理复合样式
例如:prop是"border-left-color"时的情况
调用字符串的replace方法
replace的第一个参数是一个全局匹配的正则(带有g)
replace的第二个参数传了一个回调
这个回调的第一个参数是正则整体匹配到的字符串,第二个参数是匹配到的第一个子串,第三个参数是匹配到的第二个子串...以此类推
最后,这个回调还有两个参数,倒数第二个参数是匹配到的子字符串在整个字符串中的索引
倒数第一个参数是整个字符串
这个方法的返回值最终将作为新的子串去替换掉原来字符串中匹配到的子串
如果没有写返回值,js中的函数默认将返回undefined
那么返回的字符串中会以"undefined"作为新的子串替换掉原来的匹配到的子串
就造成了意想不到的结果
关于replace的这种用法还想多说两句
如果第一个正则类型的参数如果有两个子项,例如
var prop="border-left-color"; prop.replace(/-(w)(w)/g,function(wholeChildStr,childStr1,childStr2){ return childStr1.toUpperCase(); });
而最后return回去只有一个处理了的子项
返回值将变为"borderLftClor"
因为这一个处理过的子项的值替换掉了两个子项
正确的做法应该是:
var prop="border-left-color"; prop.replace(/-(w)(w)/g,function(wholeChildStr,childStr1,childStr2){ return childStr1.toUpperCase()+childStr2; });
注意,我们刚才讨论的一直都是返回值
执行了这段代码之后prop这个字符串本身的值是不变的
回到我们的jQuery源码当中来
接下来通过elem.currentStyle[prop] || elem.currentStyle[newProp]
拿到最终的样式值
它会先尝试以elem.currentStyle["border-left-color"]这种形式读取一次
如果这个读不出来就通过elem.currentStyle["borderLeftColor"]来读取
接下来是getComputedStyle这个分支
这句话
prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase();
的作用和上面的
var prop="border-left-color"; prop.replace(/-(w)(w)/g,function(wholeChildStr,childStr1,childStr2){ return childStr1.toUpperCase()+childStr2; });
正好相反
是将形如"borderLeftColor"格式的字符串转换成"border-left-color"
接下来
var cur = document.defaultView.getComputedStyle(elem, null);
平时只是通过getComputedStyle(elem,null)["width"]这种方式直接拿来相应的值
document.defaultView.getComputedStyle(elem, null) 这个东西是真没打到控制台看过
贴出图来纪念一下吧
其实就是一个对象而已
接下来的思路我猜测大概是这样的
如果cur拿到的的确就是一个对象,那么直接通过调用getPropertyValue(prop)就会得到对应值(PS:其实我试了一下直接用cur[prop]也能拿到这个值)
一开始看到还有else if,看到else if里面判断了prop是不是display就感觉很奇怪
但是过了一会儿我反应过来了,jQuery是想要获取display为none的对象的样式
当一个元素隐藏时,cur一定是null或undefined
也就是说if条件进不了
再往下面else if走的时候就先判断是不是要去取display,如果取display的话,那自然就是none
因为元素被隐藏了
如果不是取display的话就进最后的else
在这里我们的swap函数现身了
有了上面对swap的分析,我们也可以很轻松的理解这段代码
就是先将元素display:block显示出来
然后拿到它的某个样式的值之后再给它隐藏
整个过程是非常迅速的,我们不可能看到这个元素在页面上闪烁了一瞬间就消失这种情况
(3)、css
第一眼看上去这个方法实现的功能好像和curCSS差不多,得到元素e的p样式值
而且在这个方法内部调用了curCSS方法
css: function(e,p) { if ( p == "height" || p == "width" ) { var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"]; for ( var i in d ) { old["padding" + d[i]] = 0; old["border" + d[i] + "Width"] = 0; } jQuery.swap( e, old, function() { if (jQuery.css(e,"display") != "none") { oHeight = e.offsetHeight; oWidth = e.offsetWidth; } else { e = $(e.cloneNode(true)).css({ visibility: "hidden", position: "absolute", display: "block" }).prependTo("body")[0]; oHeight = e.clientHeight; oWidth = e.clientWidth; e.parentNode.removeChild(e); } }); return p == "height" ? oHeight : oWidth; } else if ( p == "opacity" && jQuery.browser.msie ) return parseFloat( jQuery.curCSS(e,"filter").replace(/[^0-9.]/,"") ) || 1; return jQuery.curCSS( e, p ); },
仔细一看之后发现,这个css方法好像是对某些样式,比如width、height、opacity做了一些修正
jQuery对外只提供了css方法,因此平时使用的时候最好只使用css方法,curCSS方法是jQuery内部使用的一个方法
可以理解为在调用css方法获取样式值的时候先检查是不是width、height、opacity
如果是的话会做一些修正,否则的话就直接调用curCSS方法了
接下来再仔细看看css方法里面做了什么
首先判断传进来的样式名是不是height和width
如果是的话进入if里面
在if中先构造了一个old对象,最终形如:
{ "paddingTop":0, "paddingRight":0, "paddingBottom":0, "paddingLeft":0, "borderTopWidth":0, "borderRightWidth":0, "borderBottomWidth":0, "borderLeftWidth":0 }
接下来又调用了swap方法
有了上面一次用swap方法的经验,这次更深刻的认识到swap方法到底要干嘛了
这个swap先把元素e的在old对象中枚举的样式都先设置为0,即把padding和border都清零
然后做了一件事情之后又把padding和border恢复回来
那做了一件什么事呢?
就是function里面的那件事
这个function里面的if判断条件递归调用了jQuery.css方法
jQuery.css(e,"display")中的第二个参数不是 width height opacity所以直接走最后return jQuery.curCSS(e,"display")
到这里我们也知道了这个if就是用来判断元素e是显示还是隐藏
进入if里面的是e显示的情况,进入else里面的是e隐藏的情况
e如果是显示的话,那就很好办了,直接将offsetWidth和offsetHeight赋值给oWidth和oHeight
e如果是隐藏的话,就先克隆一个e对象:$(e.cloneNode(true))
传入参数true代表连同e里面的innerHTML一起克隆过来
注意接下来调用的css方法是实例化方法,而不是我们当前正在分析的jQuery.css方法了
不够这个实例化方法我们平时用的非常多,所以接下来这一句也很容易理解
把克隆出来的元素添加到body的最开头(prependTo)
由于现在e是display为none的
因此首先得先让它显示才能拿到offsetWidth offsetHeight,所以加上了display:block
然后为了不让新克隆出来的元素混淆视听,加上visibility:hidden之后让元素占位隐藏了
占位隐藏了之后页面上一定会多出一块空白,所以要通过position:absolute让占位隐藏的元素清除浮动
这样页面就和原来没有加这个元素效果一样了
废了这么大的劲拿到了clientWidth和clientHeight之后再将刚才添加到body上的那个元素remove掉
在这里有点想不明白的是为什么上面用offsetWidth和offsetHeight,而下面用clientWidth和clientHeight
更搞不明白为什么要对width和height进行修正
然后接下来的else if条件是修正低版本IE浏览器下不认opacity样式
拿到了元素的filter的值之后将除了数字和小数点之外的东西全都干掉再返回