深入理解jQuery.css和jQuery.style源码

测试代码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都是可以的!

你可能感兴趣的:(深入理解jQuery.css和jQuery.style源码)