value;
}
-function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
- var i,
- val = 0;
-
- // If we already have the right measurement, avoid augmentation
- if ( extra === ( isBorderBox ? "border" : "content" ) ) {
- i = 4;
-
- // Otherwise initialize for horizontal or vertical properties
- } else {
- i = name === "width" ? 1 : 0;
+function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
+ var i = dimension === "width" ? 1 : 0,
+ extra = 0,
+ delta = 0;
+
+ // Adjustment may not be necessary
+ if ( box === ( isBorderBox ? "border" : "content" ) ) {
+ return 0;
}
for ( ; i < 4; i += 2 ) {
- // Both box models exclude margin, so add it if we want it
- if ( extra === "margin" ) {
- val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+ // Both box models exclude margin
+ if ( box === "margin" ) {
+ delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
}
- if ( isBorderBox ) {
+ // If we get here with a content-box, we're seeking "padding" or "border" or "margin"
+ if ( !isBorderBox ) {
- // border-box includes padding, so remove it if we want content
- if ( extra === "content" ) {
- val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
- }
+ // Add padding
+ delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
- // At this point, extra isn't border nor margin, so remove border
- if ( extra !== "margin" ) {
- val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", 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 {
- // At this point, extra isn't content, so add padding
- val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+ // For "content", subtract padding
+ if ( box === "content" ) {
+ delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+ }
- // At this point, extra isn't content nor padding, so add border
- if ( extra !== "padding" ) {
- val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+ // For "content" or "padding", subtract border
+ if ( box !== "margin" ) {
+ delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
}
}
}
- return val;
+ // 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(
+ elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
+ computedVal -
+ delta -
+ extra -
+ 0.5
+ ) );
+ }
+
+ return delta;
}
-function getWidthOrHeight( elem, name, extra ) {
+function getWidthOrHeight( elem, dimension, extra ) {
// Start with computed style
var valueIsBorderBox,
styles = getStyles( elem ),
- val = curCSS( elem, name, styles ),
+ val = curCSS( elem, dimension, styles ),
isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
// Computed unit is not pixels. Stop here and return.
// Check for style in case a browser which returns unreliable values
// for getComputedStyle silently falls back to the reliable elem.style
valueIsBorderBox = isBorderBox &&
- ( support.boxSizingReliable() || val === elem.style[ name ] );
+ ( support.boxSizingReliable() || val === elem.style[ dimension ] );
// Fall back to offsetWidth/Height when value is "auto"
// This happens for inline elements with no explicit setting (gh-3571)
if ( val === "auto" ) {
- val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ];
+ val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ];
}
- // Normalize "", auto, and prepare for extra
+ // Normalize "" and auto
val = parseFloat( val ) || 0;
- // Use the active box-sizing model to add/subtract irrelevant styles
+ // Adjust for the element's box model
return ( val +
- augmentWidthOrHeight(
+ boxModelAdjustment(
elem,
- name,
+ dimension,
extra || ( isBorderBox ? "border" : "content" ),
valueIsBorderBox,
- styles
+ styles,
+
+ // Provide the current computed size to request scroll gutter calculation (gh-3589)
+ val
)
) + "px";
}
}
} );
-jQuery.each( [ "height", "width" ], function( i, name ) {
- jQuery.cssHooks[ name ] = {
+jQuery.each( [ "height", "width" ], function( i, dimension ) {
+ jQuery.cssHooks[ dimension ] = {
get: function( elem, computed, extra ) {
if ( computed ) {
// in IE throws an error.
( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
swap( elem, cssShow, function() {
- return getWidthOrHeight( elem, name, extra );
+ return getWidthOrHeight( elem, dimension, extra );
} ) :
- getWidthOrHeight( elem, name, extra );
+ getWidthOrHeight( elem, dimension, extra );
}
},
set: function( elem, value, extra ) {
var matches,
- styles = extra && getStyles( elem ),
- subtract = extra && augmentWidthOrHeight(
+ styles = getStyles( elem ),
+ subtract = extra && boxModelAdjustment(
elem,
- name,
+ dimension,
extra,
jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
styles
if ( subtract && ( matches = rcssNum.exec( value ) ) &&
( matches[ 3 ] || "px" ) !== "px" ) {
- elem.style[ name ] = value;
- value = jQuery.css( elem, name );
+ elem.style[ dimension ] = value;
+ value = jQuery.css( elem, dimension );
}
return setPositiveNumber( elem, value, subtract );
} );
} );
+QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) {
+ assert.expect( 36 );
+
+ var i,
+ suffix = "",
+ updater = function( adjustment ) {
+ return function( i, old ) {
+ return old + adjustment;
+ };
+ },
+ parent = jQuery( "<div/>" )
+ .css( { position: "absolute", width: "1000px", height: "1000px" } )
+ .appendTo( "#qunit-fixture" ),
+ fraction = jQuery( "<div style='width:4.5px;'/>" ).appendTo( parent ).width() % 1,
+ borderWidth = 1,
+ padding = 2,
+ size = 100 + fraction,
+ scrollBox = {
+ position: "absolute",
+ overflow: "scroll",
+ width: size + "px",
+ height: size + "px"
+ },
+ borderBox = {
+ border: borderWidth + "px solid blue",
+ padding: padding + "px"
+ },
+ plainContentBox = jQuery( "<div />" )
+ .css( scrollBox )
+ .css( { "box-sizing": "content-box" } )
+ .appendTo( parent ),
+ contentBox = jQuery( "<div />" )
+ .css( scrollBox )
+ .css( borderBox )
+ .css( { "box-sizing": "content-box" } )
+ .appendTo( parent ),
+ borderBox = jQuery( "<div />" )
+ .css( scrollBox )
+ .css( borderBox )
+ .css( { "box-sizing": "border-box" } )
+ .appendTo( parent ),
+ $boxes = jQuery( [ plainContentBox[ 0 ], contentBox[ 0 ], borderBox[ 0 ] ] );
+
+ for ( i = 0; i < 3; i++ ) {
+ if ( i === 1 ) {
+ suffix = " after increasing inner* by " + i;
+ size += i;
+ $boxes.innerWidth( updater( i ) ).innerHeight( updater( i ) );
+ } else if ( i === 2 ) {
+ suffix = " after increasing outer* by " + i;
+ size += i;
+ $boxes.outerWidth( updater( i ) ).outerHeight( updater( i ) );
+ }
+
+ assert.equal( plainContentBox.innerWidth(), size,
+ "plain content-box innerWidth includes scroll gutter" + suffix );
+ assert.equal( plainContentBox.innerHeight(), size,
+ "plain content-box innerHeight includes scroll gutter" + suffix );
+ assert.equal( plainContentBox.outerWidth(), size,
+ "plain content-box outerWidth includes scroll gutter" + suffix );
+ assert.equal( plainContentBox.outerHeight(), size,
+ "plain content-box outerHeight includes scroll gutter" + suffix );
+
+ assert.equal( contentBox.innerWidth(), size + 2 * padding,
+ "content-box innerWidth includes scroll gutter" + suffix );
+ assert.equal( contentBox.innerHeight(), size + 2 * padding,
+ "content-box innerHeight includes scroll gutter" + suffix );
+ assert.equal( contentBox.outerWidth(), size + 2 * padding + 2 * borderWidth,
+ "content-box outerWidth includes scroll gutter" + suffix );
+ assert.equal( contentBox.outerHeight(), size + 2 * padding + 2 * borderWidth,
+ "content-box outerHeight includes scroll gutter" + suffix );
+
+ assert.equal( borderBox.innerWidth(), size - 2 * borderWidth,
+ "border-box innerWidth includes scroll gutter" + suffix );
+ assert.equal( borderBox.innerHeight(), size - 2 * borderWidth,
+ "border-box innerHeight includes scroll gutter" + suffix );
+ assert.equal( borderBox.outerWidth(), size,
+ "border-box outerWidth includes scroll gutter" + suffix );
+ assert.equal( borderBox.outerHeight(), size,
+ "border-box outerHeight includes scroll gutter" + suffix );
+ }
+} );
+
} )();