Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dimensions: Include scroll gutter in "padding" box #3656

Merged
merged 4 commits into from
Jun 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 68 additions & 46 deletions src/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,58 +80,77 @@ function setPositiveNumber( elem, value, subtract ) {
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.
Expand All @@ -142,25 +161,28 @@ function getWidthOrHeight( elem, name, extra ) {
// 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";
}
Expand Down Expand Up @@ -321,8 +343,8 @@ jQuery.extend( {
}
} );

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 ) {

Expand All @@ -338,18 +360,18 @@ jQuery.each( [ "height", "width" ], function( i, name ) {
// 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
Expand All @@ -359,8 +381,8 @@ jQuery.each( [ "height", "width" ], function( i, name ) {
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 );
Expand Down
83 changes: 83 additions & 0 deletions test/unit/dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,4 +552,87 @@ QUnit.test( "width/height on an inline element with no explicitly-set dimensions
} );
} );

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 );
}
} );

} )();