测试代码1:
JS部分:
$(function() { //在IE中要用驼峰写法!打印10% //alert(document.getElementById("content").currentStyle["marginRight"]); //css方法已经把100%转化为绝对值了66px //alert($("#content").css("margin-right")); //打印66px //alert($("#content").css("margin-left")); //如果是auto那么就是auto alert($("#content").css("margin-top")); //alert($("#content")[0].currentStyle["marginTop"]); //style获取也是auto //alert($("#content")[0].style["marginTop"]); //var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; //alert($("#content").css("marginTop")); //alert($("#content").css("marginLeft")); })HTML部分:
<html> <head> <script type="text/javascript" src="/jquery/jquery.js"></script> </head> <body> <div id="content" style="margin-right:10%;border:1px solid red;height:100px;margin-left:10%;margin-top:10%;"></div> </body> </html>测试代码2:
HTML部分:
<div id="body" style="border:1px solid red;width:100%;height:100px;margin-right:auto;float:left;font-size:1em;margin-top:20%;left:100px;"> </div>JS部分:
var rposition = /^(top|right|bottom|left)$/; var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); function curCSS( elem, name, computed ) { var left, rs, rsLeft, ret, style = elem.style; //获取到currentStyle对象(注意,第三个参数可以直接传入style对象作为computed computed = computed || getStyles( elem ); //如果currentStyle存在,获取属性值 ret = computed ? computed[ name ] : undefined // Avoid setting ret to empty string here // so we don't default to auto //元素的style对象存在,style的name属性值存在,但是currentStyle相关属性不存在 //那么把结果赋值为style对象的相关属性! //alert(computed[name]+"->"+style[name]); if ( ret == null && style && style[ name ] ) { ret = style[ name ]; } // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels // but not position css attributes, as those are proportional to the parent element instead // and we can't measure the parent instead because it might trigger a "stacking dolls" problem if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { // Remember the original values left = style.left; rs = elem.runtimeStyle; rsLeft = rs && rs.left; // Put in the new values to get a computed value out if ( rsLeft ) { //IE用currentStyle获取cascadeStyle,并且赋值! rs.left = elem.currentStyle.left; } //如果名字name是fontSize那么left赋值为13em,否则就是ret的值! style.left = name === "fontSize" ? "1em" : ret; ret = style.pixelLeft + "px"; // Revert the changed values style.left = left; if ( rsLeft ) { rs.left = rsLeft; } }//END OF IF return ret; } function MyStyle(elem,name) { var ret; if ( document.documentElement.currentStyle ) { //用元素的currentStyle就可以了! getStyles = function( elem ) { return elem.currentStyle; }; ret=curCSS(elem,name); } // Support: IE // IE returns zIndex value as an integer. return ret === undefined ? ret : ret + "" || "auto"; } alert(MyStyle(document.getElementById("body"),"marginTop"));//打印的是绝对值! //alert(MyStyle(document.getElementById("body"),"marginRight")); //alert(MyStyle(document.getElementById("body"),"width")); //alert(MyStyle(document.getElementById("body"),"fontSize")); //alert("style->"+document.getElementById("body").style["marginRight"]); //打印像素值,getComputedStyle打印是像素值,但是currentStyle打印的还是相对值! alert(window.getComputedStyle(document.getElementById("body"),"")["width"]); //打印100%; alert(document.getElementById("body").style.width); //也被计算出来了特定的像素值! alert(window.getComputedStyle(document.getElementById("body"),"")["margin-left"]);*/总结:
我把jQuery源码中针对IE浏览器的处理代码逻辑提炼了出来进行分析,结果表明如%,em等单位都被转化为了像素,那么底层是怎么实现了呢。我们知道在非IE浏览器中getComputedStyle获取到的就是像素值,也就是绝对性,那么不需要针对他们进行特殊的处理,但是IE不同,因为IE通过currentStyle获取到的是cascade style也就是%等,所以需要把他转化为特定的像素值!于是这里引入了 style.pixelLeft方法就是只是获取像素值!那么你可能会问,上面只要通过正则表达式 rnumnonpx.test( ret ) && !rposition.test( name )验证(如%,em同时不是获取的top,left,bottom,right属性),就会执行if语句,那么if语句里为什么一直抓住元素的left属性不放呢?如获取style.left和runtimeStyle.left。我的理解是:
假如我们获取的是margin-right:20%那么就会满足这个正则表达式,那么首先把style.left和runtimeStyle.left保存下来,第二步就是:把runtimeStyle的left设置为currentStyle.left,为什么呢,因为runtimeStyle的优先级比style设置的优先级高,style比head中的css属性更高,head中的css比外链的优先级更高,如果把runtimeStyle的left修改为currentStyle.left那么相当于我们要获取到的其实是currentStyle的left属性,而屏蔽掉runtimeStyle的left属性,从而获取到一个新的页面布局信息(因为上面对runtimeStyle的修改会引起页面重新排版获取到最新的页面信息)
那么我们为什么要抓住left属性不放呢?如果还是我上面说的margin-right:20%这种情况,那么在源码中就会被设置为style.left=20%,这个20%也是相对于父元素来说的,所以我直接把left设置为20%也就是获取到父元素20%的值!这时候直接通过style.pixelLeft就能够获取到父元素20%是多少了!
你可能会问,如果是margin-top,margin-bottom那么在css中不是相对于父元素的高度来说的吗?我想说还真的不是!在css中如果把margin-top设置为10%他还是相当于父元素的宽度来说的!只是这时候是相对于内容而不包括border和margin(IE中不包括border因为IE的width已经包括border了所以再设置border还是不变),然后margin-top的值是前面的内容宽度×10%+2*border-width(父元素的左右边框的值),所以在jQuery中才只是关注了用left!下面的例子中:
例子:
CSS部分:
body { width:1200px; }HTML部分:
<div id="body" style="border:1px solid red;width:100%;height:100px;margin-right:auto;font-size:1em;margin-top:10%;left:100px;"> </div>JS部分:
var rposition = /^(top|right|bottom|left)$/; var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); function curCSS( elem, name, computed ) { var left, rs, rsLeft, ret, style = elem.style; //获取到currentStyle对象(注意,第三个参数可以直接传入style对象作为computed computed = computed || getStyles( elem ); //如果currentStyle存在,获取属性值 ret = computed ? computed[ name ] : undefined // Avoid setting ret to empty string here // so we don't default to auto //元素的style对象存在,style的name属性值存在,但是currentStyle相关属性不存在 //那么把结果赋值为style对象的相关属性! //alert(computed[name]+"->"+style[name]); if ( ret == null && style && style[ name ] ) { ret = style[ name ]; } // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels // but not position css attributes, as those are proportional to the parent element instead // and we can't measure the parent instead because it might trigger a "stacking dolls" problem if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { // Remember the original values left = style.left; rs = elem.runtimeStyle; rsLeft = rs && rs.left; // Put in the new values to get a computed value out if ( rsLeft ) { //IE用currentStyle获取cascadeStyle,并且赋值!如果不把runtimeStyle覆盖那么不可能获取获取到style中的值 rs.left = elem.currentStyle.left; } //如果名字name是fontSize那么left赋值为13em,否则就是ret的值! style.left = name === "fontSize" ? "1em" : ret; ret = style.pixelLeft + "px"; // Revert the changed values style.left = left; if ( rsLeft ) { rs.left = rsLeft; } }//END OF IF return ret; } function MyStyle(elem,name) { var ret; if ( document.documentElement.currentStyle ) { //用元素的currentStyle就可以了! getStyles = function( elem ) { return elem.currentStyle; }; ret=curCSS(elem,name); } // Support: IE // IE returns zIndex value as an integer. return ret === undefined ? ret : ret + "" || "auto"; } alert(MyStyle(document.getElementById("body"),"marginTop"));测试结果:
(1)在IE中margin-top只是120px,也就是内容的宽度为1200×10%=120px;在其它浏览器中也是一样的都是父元素的内容宽度的相应的百分比,不包括父元素的border和margin的值!
(2)强烈建议在IE中margin-top等用驼峰写法,同时在其它浏览器中也用驼峰写法!可以把相对的宽度等通过getComputedStyle转化为绝对值,但是在IE中currentStyle只能是相对值,jQuery的做法就是用了pixelTop等!
(3)至于pageXoffset和pageYoffset等表示页面的滚动情况!
(4)在IE中用pixelRight属性获取元素的right的属性值,但是是整数类型也就是不包含px单位!pixelLeft,pixelTop,pixelBottom,pixelWidth,pixelHeight返回的是元素的高度和宽度的像素值都是整数类型。(可以在上面的博客中选择)posLeft,posRight,posTop,posBottom等只是将left,right属性转化为浮点类型。pageXoffset,pageYOffset表示文档滚动的距离(IE浏览器),pageX,pageY等获取鼠标在文档中的坐标!
在非IE浏览器中jQuery.css的逻辑代码:
if ( window.getComputedStyle ) { 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 ); }; curCSS = function( elem, name, computed ) { var width, minWidth, maxWidth, ret, style = elem.style; computed = computed || getStyles( elem ); // getPropertyValue is only needed for .css('filter') in IE9, see #12537 ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined; if ( computed ) { if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } // A tribute to the "awesome hack by Dean Edwards" // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right // Safari 5.1.7 (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 ) ) { // Remember the original values width = style.width; minWidth = style.minWidth; maxWidth = style.maxWidth; // Put in the new values to get a computed value out style.minWidth = style.maxWidth = style.width = ret; ret = computed.width; // Revert the changed values style.width = width; style.minWidth = minWidth; style.maxWidth = maxWidth; } } // Support: IE // IE returns zIndex value as an integer. return ret === undefined ? ret : ret + ""; };总结:
(1)在非IE浏览器走的是上面的逻辑,margin-left,margin-right等这种%或者em等相对大小的处理是把style属性的值通过getComputedStyle的绝对值进行覆盖进而让浏览器进行重绘和重排版,进而获取新的值!也就是说在有些浏览器中如果是margin开头的属性同时也是%或者em等相对的大小我们就用getComputedStyle的值去覆盖style的相应的值!
(2)至于为什么一直是抓住width属性不放是因为这时候的margin-right,margin-top等是相对于父元素的宽度来说的,记住都是相对于宽度!这和IE中一直抓住left属性的值是一样的道理!
(3)建议连pageX/pageY,layerX,layerY等一次性解决!
(4)currentStyle获取的是cascadeStyle!
(5)IE用的是styleFloat,其它浏览器用的是cssFloat
(6)默认字体大小12px
(7)低版本IE中如果要通过style或者currentStyle访问margin-right那么必须用style.marginRight或者style["marginRight"]不能用style["margin-right"]否则为undefined!用getComputedStyle都是可以的!