内容简介:一、在讲之前,先弄清 boxSizing 属性(1)box-sizing 是默认值 "content-box"
一、在讲之前,先弄清 boxSizing 属性
(1)box-sizing 是默认值 "content-box"
<body> <script src="jQuery.js"></script> <div id="pTwo" style="width: 55px; border:1px red solid;">这是divTwo</div> <script> $("#pTwo").width() //55 </script> </body>
$().width()
的值是 55
(2)box-sizing 是 "border-box"
<div id="pTwo" style="width: 55px; box-sizing: border-box; border:1px red solid;">这是divTwo</div>
$().width()
的值是 53
因为 border-box 是包括 border、padding、content 的,而 content-box 只包括 content。
可想而知,jQuery的 $()
.width() 中也包含了对 borderBox 的判断。
- 注意下
div
标签的默认值
二、 $()
.width()
作用:
获取目标元素的宽度
源码:
//源码7033行 //$.each(obj,callback(index,item){}) jQuery.each( [ "height", "width" ], function( i, dimension ) { //i:0 dimension:height //i:1 dimension:width //cssHooks是用来定义style方法的 jQuery.cssHooks[ dimension ] = { //读 //$().width() //参数:elem:目标DOM元素/computed:true/extra:"content" get: function( elem, computed, extra ) { console.log(elem, computed, extra,'extra7040') if ( computed ) { // 某些元素是有尺寸的信息的,如果我们隐式地显示它们,前提是它必须有一个display值 // Certain elements can have dimension info if we invisibly show them // but it must have a current display style that would benefit // 上面这句话的意思是,某个元素用display:none,将它从页面上去掉了,此时是获取不到它的宽度的 // 如果要获取它的宽度的话,需要隐式地显示它们,比如display:absolute,visible:hidden // 然后再去获取它的宽度 // block:false // none:true // rdisplayswap的作用是检测 none和table开头的 return rdisplayswap.test( jQuery.css( elem, "display" ) ) && // 兼容性的考虑,直接看 getWidthOrHeight // Support: Safari 8+ // Table columns in Safari have non-zero offsetWidth & zero // getBoundingClientRect().width unless display is changed. // Support: IE <=11 only // Running getBoundingClientRect on a disconnected node // in IE throws an error. // display为none的话,elem.getBoundingClientRect().width=0 // elem.getClientRects() 返回CSS边框的集合 // https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getClientRects ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? swap( elem, cssShow, function() { return getWidthOrHeight( elem, dimension, extra ); } ) : //$().width()情况 //dimension:width/extra:"content" getWidthOrHeight( elem, dimension, extra ); } }, }; } );
解析:
(1) box-sizing 是默认值,并且 display 不为 none
①rdisplayswap
作用:
检测目标元素的 display
属性的值 是否为 none
或以 table
开头
// 检测 display 的值是否为 none 或以 table 开头 // Swappable if display is none or starts with table // 除了 "table", "table-cell", "table-caption" // except "table", "table-cell", or "table-caption" // display 的值,请访问 https://developer.mozilla.org/en-US/docs/CSS/display // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display // 源码6698行 var rdisplayswap = /^(none|table(?!-c[ea]).+)/,
如果 display
是 none
的话,就会调用 swap()
方法,反之,就直接调用 getWidthOrHeight()
方法
getWidthOrHeight()
作用:
获取 width
或 height
的值
//获取 width 或 height //dimension:width/extra:"content" //源码6823行 function getWidthOrHeight( elem, dimension, extra ) { // Start with computed style var styles = getStyles( elem ), val = curCSS( elem, dimension, styles ), //判断 box-sizing 的值是否 是 border-box //如果启用了 box-sizing,js 的 width 是会算上 margin、border、padding的 //如果不启用的话,js 的 width 只会算 content //jQuery 的 width 自始至终都是算的 content isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", valueIsBorderBox = isBorderBox; //火狐兼容性处理,可不看 // Support: Firefox <=54 // Return a confounding non-pixel value or feign ignorance, as appropriate. if ( rnumnonpx.test( val ) ) { if ( !extra ) { return val; } val = "auto"; } // 通过getComputedStyle检查style属性,并返回可靠的style属性,这样可以防止浏览器返回不可靠的值 // Check for style in case a browser which returns unreliable values // for getComputedStyle silently falls back to the reliable elem.style valueIsBorderBox = valueIsBorderBox && ( support.boxSizingReliable() || val === elem.style[ dimension ] ); console.log(valueIsBorderBox,'valueIsBorderBox6853') // Fall back to offsetWidth/offsetHeight when value is "auto" // This happens for inline elements with no explicit setting (gh-3571) // Support: Android <=4.1 - 4.3 only // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) if ( val === "auto" || !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) { val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ]; console.log(val,'val6862') // offsetWidth/offsetHeight provide border-box values valueIsBorderBox = true; } // Normalize "" and auto // 55px val = parseFloat( val ) || 0; console.log(val,extra,'val6869') // Adjust for the element's box model return ( val + boxModelAdjustment( //DOM节点 elem, //width dimension, //content extra || ( isBorderBox ? "border" : "content" ), //true/false valueIsBorderBox, //styles styles, //55 // Provide the current computed size to request scroll gutter calculation (gh-3589) val ) ) + "px"; }
getWidthOrHeight() 里面有好多方法,我们一一来解析:
getStyles( elem )
作用:
获取该 DOM 元素的所有 css 属性的值
//获取该DOM元素的所有css属性的值 //源码6501行 var getStyles = function( elem ) { // 兼容性处理,旨在拿到正确的view // Support: IE <=11 only, Firefox <=30 (#15098, #14150) // IE throws on elements created in popups // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" var view = elem.ownerDocument.defaultView; if ( !view || !view.opener ) { view = window; } //获取所有CSS属性的值 return view.getComputedStyle( elem ); };
可以看到,本质是调用了 getComputedStyle()
方法。
curCSS( elem, dimension, styles )
作用:
获取元素的当前属性的值
// 获取元素的当前属性的值 // elem, "position" // elem,width,styles // 源码6609行 function curCSS( elem, name, computed ) { var width, minWidth, maxWidth, ret, // Support: Firefox 51+ // Retrieving style before computed somehow // fixes an issue with getting wrong values // on detached elements style = elem.style; //获取elem所有的样式属性 computed = computed || getStyles( elem ); // console.log(computed,'computed6621') // getPropertyValue is needed for: // .css('filter') (IE 9 only, #12537) // .css('--customProperty) (#3144) if ( computed ) { //返回元素的属性的当前值 //position:static //top:0px //left:0px ret = computed.getPropertyValue( name ) || computed[ name ]; console.log(ret,'ret6627') //如果目标属性值为空并且目标元素不在目标元素所在的文档内(感觉这种情况好奇怪) if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { //使用jQuery.style方法来获取目标元素的属性值 ret = jQuery.style( elem, name ); } // A tribute to the "awesome hack by Dean Edwards" // Android Browser returns percentage for some values, // but width seems to be reliably pixels. // This is against the CSSOM draft spec: // https://drafts.csswg.org/cssom/#resolved-values //当属性设置成数值时,安卓浏览器会返回一些百分比,但是宽度是像素显示的 //这违反了CSSOM草案规范 //所以以下方法是修复不规范的width属性的 if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.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; } } return ret !== undefined ? // 兼容性,IE下返回的zIndex的值是数字, // 而使用jQuery获取的属性都是返回字符串 // Support: IE <=9 - 11 only // IE returns zIndex value as an integer. ret + "" : ret; }
可以看到, curCSS
本质是调用了 computed.getPropertyValue( name )
方法,也就是说我们可以这样去获取目标元素的属性值:
let a=document.getElementById("pTwo") a.ownerDocument.defaultView.getComputedStyle(a).getPropertyValue('width') //55px
目标元素的所属 view,调用 getComputedStyle()
方法,获取目标元素的所有 CSS 属性,再调用 getPropertyValue('width')
,获取目标 width
的属性值,为 55px
注意:无论 box-sizing
的值是 border-box
还是 content-box
,上面的方法获取的 width
值都是 55px
,这是不符合 CSS3 盒子模型的,所以 jQuery 拿到该值后,还会继续处理。
⑤ boxModelAdjustment
因为这里讨论的是情况一,所以 boxModelAdjustment()
会直接返回 0
综上:当 box-sizing 是默认值,并且 display 不为 none
时,返回的 width
是:
parseFloat(a.ownerDocument.defaultView.getComputedStyle(a).getPropertyValue('width')) //55
(2)box-sizing 值为 border-box
<div id="pTwo" style="width: 55px; margin-left:2px; padding-left: 2px; box-sizing: border-box; border:1px red solid;">这是divTwo</div>
$("#pTwo").width() //51 document.getElementById("pTwo").style.width //55px
可以看到,原生 js 获取 width 是不遵循 CSS3 盒子规范的。
borderBox 的判断在 getWidthOrHeight()
方法中,直接看过去:
//获取 width 或 height //dimension:width/extra:"content" //源码6823行 function getWidthOrHeight( elem, dimension, extra ) { xxx ... var styles = getStyles( elem ), //true isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", //true valueIsBorderBox = isBorderBox; xxx ... valueIsBorderBox = valueIsBorderBox && //val值是通过a.ownerDocument.defaultView.getComputedStyle(a).getPropertyValue('width')得出的 //但又通过js原生的style.width来取值并与val相比较 ( support.boxSizingReliable() || val === elem.style[ dimension ] ); console.log(val === elem.style[ dimension ],'valueIsBorderBox6853') // 55 val = parseFloat( val ) || 0; // Adjust for the element's box model return ( val + //borderBox走这里 boxModelAdjustment( //DOM节点 elem, //width dimension, //content extra || ( isBorderBox ? "border" : "content" ), //true/false valueIsBorderBox, //styles styles, //55 // Provide the current computed size to request scroll gutter calculation (gh-3589) val ) ) + "px"; }
boxModelAdjustment():
作用:
集中处理borderBox的情况
//参数说明: //elem:DOM节点/dimension:width/box:content/isBorderBox:true/false/styles:styles/computedVal:55 //源码6758行 function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { var i = dimension === "width" ? 1 : 0, extra = 0, delta = 0; // 如果 boxSizing 的属性值,而不是 borderBox 的话,就直接返回 0 // Adjustment may not be necessary if ( box === ( isBorderBox ? "border" : "content" ) ) { console.log('content1111','content6768') return 0; } //小技巧 //i 的初始值是 0/1 //然后 cssExpand = [ "Top", "Right", "Bottom", "Left" ] for ( ; i < 4; i += 2 ) { // Both box models exclude margin if ( box === "margin" ) { //var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; //width 的话,就是 marginRight/marginLeft //height 的话,就是 marginTop/marginBottom //jQuery.css( elem, box + cssExpand[ i ], true, styles ) 的意思就是 //返回 marginRight/marginLeft/marginTop/marginBottom 的数字,并给 delta 加上 delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); } // If we get here with a content-box, we're seeking "padding" or "border" or "margin" // 如果不是 borderBox 的话 if ( !isBorderBox ) { // Add padding // 添加 padding-xxx delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); // For "border" or "margin", add border if ( box !== "padding" ) { delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); // But still keep track of it otherwise } else { extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } // If we get here with a border-box (content + padding + border), we're seeking "content" or // "padding" or "margin" } else { // 去掉 padding // For "content", subtract padding if ( box === "content" ) { //width,去掉paddingLeft,paddingRight的值 delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } // For "content" or "padding", subtract border // 去掉 borderXXXWidth if ( box !== "margin" ) { //width,去掉borderLeftWidth,borderRightWidth的值 delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } } // Account for positive content-box scroll gutter when requested by providing computedVal if ( !isBorderBox && computedVal >= 0 ) { // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border // Assuming integer scroll gutter, subtract the rest and round down delta += Math.max( 0, Math.ceil( //就是将dimension的首字母做个大写 elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - computedVal - delta - extra - 0.5 ) ); } return delta; }
可以看到,isBorderBox 为 true 的话,会执行下面两段代码:
if ( box === "content" ) { //width,去掉paddingLeft,paddingRight的值 delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); }
if ( box !== "margin" ) { //width,去掉borderLeftWidth,borderRightWidth的值 delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); }
去除了 paddingLeft
、 paddingRight
、 borderLeftWidth
和 borderRightWidth
,并最终返回值
二、 $()
.width(xxx)
作用:
设置目标元素的宽度
源码:
//源码7033行 //$.each(obj,callback(index,item){}) jQuery.each( [ "height", "width" ], function( i, dimension ) { //i:0 dimension:height //i:1 dimension:width //cssHooks是用来定义style方法的 jQuery.cssHooks[ dimension ] = { //写 //$().width(55) //elem:DOM节点,value:55,extra:content set: function( elem, value, extra ) { var matches, styles = getStyles( elem ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", //-4 subtract = extra && boxModelAdjustment( elem, dimension, extra, isBorderBox, styles ); // 如果是 borderBox 的话,通过 offset 计算的尺寸是不准的, // 所以要假设成 content-box 来获取 border 和 padding // Account for unreliable border-box dimensions by comparing offset* to computed and // faking a content-box to get border and padding (gh-3699) //true true 'static' //调整 subtract if ( isBorderBox && support.scrollboxSize() === styles.position ) { subtract -= Math.ceil( elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - parseFloat( styles[ dimension ] ) - boxModelAdjustment( elem, dimension, "border", false, styles ) - 0.5 ); console.log(subtract,'subtract7169') } // 如果需要进行值调整,则转换为像素 // Convert to pixels if value adjustment is needed //如果是 borderBox 并且 value 的单位不是 px,则会转换成像素 if ( subtract && ( matches = rcssNum.exec( value ) ) && ( matches[ 3 ] || "px" ) !== "px" ) { elem.style[ dimension ] = value; value = jQuery.css( elem, dimension ); } //59px return setPositiveNumber( elem, value, subtract ); } }; } );
解析:
(1)整体上看,实际上两个 if ,最后再 return 一个 setPositiveNumber()
方法
(2)注意 subtract
,如果有 borderBox 属性,并且 borderWidth、padding 有值的话,subtract 一般为负数,比如下面的例子,subtract = -4
<div id="pTwo" style="width: 55px; margin-left:2px; padding-left: 2px; box-sizing: border-box; /*box-sizing: content-box;*/ /*display: none;*/ border:1px red solid;"> 这是divTwo </div> $("#pTwo").width(55)
反之则会是 0
(3)两个 if 我试了下,都会去执行,所以直接看的 setPositiveNumber ()
setPositiveNumber:
作用:
设置真正的 width 值
function setPositiveNumber( elem, value, subtract ) { // 标准化相对值 // Any relative (+/-) values have already been // normalized at this point //[ // "55px", // undefined, // "55", // "px", // index: 0, // input: "55px", // groups: undefined, // index: 0 // input: "55px" // ] var matches = rcssNum.exec( value ); console.log(matches,( subtract || 0 ),'matches6760') return matches ? //(0,55-(-4))+'px' //Math.max(a,b) 返回两个指定的数中带有较大的值的那个数 // Guard against undefined "subtract", e.g., when used as in cssHooks Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : value; }
如果是 borderBox,width 会设置成 59px(虽然表面上开发者设置的是 $("#pTwo").width(55)
),反之,则是 55px
总结:
1、 $()
.width()
borderBox
$().width()= parseFloat(elem.ownerDocument.defaultView.getComputedStyle(elem).getPropertyValue('width'))
(2)是 borderBox()
在(1)的基础上执行 boxModelAdjustment()
方法,去除 borderWidth、padding
2、 $()
.width(xxx)
borderBox
width=xxx
(2)是borderBox
width=xxx+ setPositiveNumber()
(完)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- ReactNative源码解析-初识源码
- Spring源码系列:BeanDefinition源码解析
- Spring源码分析:AOP源码解析(下篇)
- Spring源码分析:AOP源码解析(上篇)
- 注册中心 Eureka 源码解析 —— EndPoint 与 解析器
- 新一代Json解析库Moshi源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。