jQuery源码分析之width,height,innerWidth,innerHieght,outerWidth,outerHeight函数

提前阅读:点击打开链接

(1)源码分析如下:

<pre name="code" class="javascript">jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
//{padding:innerHeight,content:height,"":outerHeight}
//{padding:innerWidth,content:width,"":outerWidth}
	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
		// margin is only for outerHeight, outerWidth
		//funcName是innerHeight,height,outerHeight,defaultExtra是padding,content,""
		//funcName是innerWidth,width,outerWidth,defaultExtra是padding,content,""
		jQuery.fn[ funcName ] = function( margin, value ) {
			//每一个函数接受两个参数margin,value。如果不传入参数那么chainable为false
			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
				//exta默认是defaultExtra
				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
             //调用底层的access方法
			return jQuery.access( this, function( elem, type, value ) {
				var doc;
                //如果是DOM是window那么获取window.document.documentElement("clientWidth")
				if ( jQuery.isWindow( elem ) ) {
					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
					// isn't a whole lot we can do. See pull request at this URL for discussion:
					// https://github.com/jquery/jquery/pull/764
					return elem.document.documentElement[ "client" + name ];
				}

				// Get document width or height
				//如果DOM是document那么首先获取documentElement,返回document.body.scrollWidth,document.scrollWidth,
				//document.body.offsetWidth,document.offsetWidth和document.clientWidth最大值!
				if ( elem.nodeType === 9 ) {
					doc = elem.documentElement;
				// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
			// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
					return Math.max(
						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
						elem.body[ "offset" + name ], doc[ "offset" + name ],
						doc[ "client" + name ]
					);
				}
                 //如果value是undefined,表示获取数据,调用jQuery.css完成,否则调用jQuery.style完成
				return value === undefined ?
					// Get width or height on the element, requesting but not forcing parseFloat
					jQuery.css( elem, type, extra ) :
					// Set width or height on the element
					jQuery.style( elem, type, value, extra );
			}, type, chainable ? margin : undefined, chainable, null );
		};
	});
});
//调用方式1:
//innerWidth如果没有传入参数表示获取属性,底层调用access方法
//alert($("#content").innerWidth());

//调用方式2:
//把宽度设为150px,chinable是true,传入access的value是150px
//在access里面调用fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
//表示fn表示上面的回调函数,fn(elems[i],"width","150")第一个表示DOM元素
//$("#content").innerWidth(150);

//调用方式3:把innerWidth至少设置为1000
//这时候margin是函数,chainable是padding,传入access的margin是函数。也就是key是函数
//在access里面raw是false,调用为  fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );  
//第一步:调用fn,就是上面access里面的第二个参数函数,传入DOM和key="width"也就是获取每一个DOM的width值
//第二步:根据获取的值调用我们自己的为innerWidth传入的参数函数,上下文为DOM,第一个参数是DOM下标,第二个参数是该元素的width值!
//第三步:把调用的结果再次调用access的第二个函数参数,第一个参数是DOM,第二个参数是"width",第三个参数是第二步返回的值,这时候value不是空,表示设置!
//$("#content").innerWidth(function(index,innerWidth){
//	 return Math.max(innerWidth, 1000);    
//})
alert($("#content").outerWidth());
 

总结:

(1)如果DOM是document那么首先获取documentElement,然后返回document.body.scrollWidth,document.scrollWidth,document.body.offsetWidth,document.offsetWidth和document.clientWidth最大值!

(2)两层each函数的关系图为:

(3)根据后面的jQuery.css的源码分析知道这里的defaultExtra要么为空字符串要么就是非空,所以都会经过parseFloat操作!如果parseFloat之后是Number类型那么就返回number值,否则返回初始值!

vendorPropName源码:

var cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
function vendorPropName( style, name ) {
	// shortcut for names that are not vendor prefixed
	if ( name in style ) {
		return name;
	}	
     //将name首字母大写,同时用origName保存没有首字母大写之前的名字
	// check for vendor prefixed names
	var capName = name.charAt(0).toUpperCase() + name.slice(1),
		origName = name,
		i = cssPrefixes.length;
	while ( i-- ) {
		name = cssPrefixes[ i ] + capName;
		if ( name in style ) {
			return name;
		}
	}
	return origName;
}  
//打印[object CSSStyleDeclaration]
//alert($("#content")[0].style);
alert(vendorPropName($("#content")[0].style,"-webkit-border-radius"));
jQuery.cssHooks源码:

cssHooks: {
		opacity: {
			get: function( elem, computed ) {
				if ( computed ) {
					// We should always get a number back from opacity
					var ret = curCSS( elem, "opacity" );
					return ret === "" ? "1" : ret;
				}
			}
		}
	}
jQuery.cssProps源码:

cssProps: {
		// normalize float css property
		"float": support.cssFloat ? "cssFloat" : "styleFloat"
	}
jQuery.cssNormalTransform源码:

	cssNormalTransform = {
			letterSpacing: "0",//如果letterSpacing是normal那么设置为0
			fontWeight: "400"//如果font-weight是normal,那么把它设置为400
		}
curCss源码:

<pre name="code" class="javascript">//如果window有getComputedStyle方法
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 );
		}
       //返回[object CSSStyleDeclaration]
		return window.getComputedStyle( elem, null );
	};

//调用:val = curCSS( elem, name, styles );
	curCSS = function( elem, name, computed ) {
		var width, minWidth, maxWidth, ret,
			style = elem.style;
		computed = computed || getStyles( elem );
        //如果getComputedStyle返回值存在那么继续调用getPropertyValue就可以了,在IE9
		//中直接用下标调用可以了,如果getComputedStyle不存在,那么返回undefined
		// getPropertyValue is only needed for .css('filter') in IE9, see #12537
		ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;   
		if ( computed ) {
             //如果获取到的属性值为空字符串同时该元素所在的文档不包括自身,那么调用jQuery.style
			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
		   //var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
		   //var rmargin = (/^margin/);
		   //chrome<17和Safari5特殊处理,通过调用getComputedStyle获取元素的cssDeclaration对象,然后获取该对象的width
			if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
				// Remember the original values
				//记住原始的width,minWidth,maxWidh
				width = style.width;
				minWidth = style.minWidth;
				maxWidth = style.maxWidth;
                 //对minWidth,maxWidth,width赋值为新值,该值通过getComputedStyle方法获取到
				// 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 + "";
	};//End of curCss function
	//如果getComputedStyle不存在,那么判断document.documentElement.currentStyle
} else if ( document.documentElement.currentStyle ) {
	//用元素的currentStyle就可以了!
	getStyles = function( elem ) {
		return elem.currentStyle;
	};

//调用:val = curCSS( elem, name, styles );
	curCSS = function( 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对象的相关属性!
		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
		  //var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
		   //var rmargin = (/^margin/);
		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 ) {
				rs.left = elem.currentStyle.left;
			}
			style.left = name === "fontSize" ? "1em" : ret;
			ret = style.pixelLeft + "px";
			// Revert the changed values
			style.left = left;
			if ( rsLeft ) {
				rs.left = rsLeft;
			}
		}
		// Support: IE
		// IE returns zIndex value as an integer.
		return ret === undefined ?
			ret :
			ret + "" || "auto";
	};
}

 curCss方法总结: 

(1)获取元素的style属性,如果window上有getComputedStyle就直接调用,但是调用的时候为了支持IE<11,FF<30+我们会判断elem.ownerDocument.defaultView.opener是否存在,存在就调用elem.ownerDocument.defaultView.getComputedStyle方法
(2)currentStyle是IE浏览器自娱自乐的一个属性,其与element.style可以说是近亲,至少在使用形式上类似,element.currentStyle,差别在于element.currentStyle返回的是元素当前应用的最终CSS属性值(包括外链CSS文件,页面中嵌入的<style>属性等)。
(3)getPropertyValue方法可以获取CSS样式申明对象上的属性值(直接属性名称),例如:
window.getComputedStyle(element, null).getPropertyValue("float");
如果我们不使用getPropertyValue方法,直接使用键值访问,其实也是可以的。但是,比如这里的的float,如果使用键值访问,则不能直接使用getComputedStyle(element, null).float,而应该是cssFloat与styleFloat,自然需要浏览器判断了,比较折腾!
使用getPropertyValue方法不必可以驼峰书写形式(不支持驼峰写法),例如:style.getPropertyValue("border-top-left-radius");参见点击打开链接


(4)width重新设置为新的值以后浏览器会进行重排版,chrome<17和safari5.0用这种方式获取到margin-right等!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值