diff --git a/src/css.js b/src/css.js index 24ddb6708..43ba09cd3 100644 --- a/src/css.js +++ b/src/css.js @@ -115,21 +115,28 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { function getWidthOrHeight( elem, name, extra ) { // Start with offset property, which is equivalent to the border-box value - var valueIsBorderBox = true, - val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + var val, + valueIsBorderBox = true, styles = getStyles( elem ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + if ( support.gBCRDimensions() ) { + // Support: IE <= 11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + if ( elem.getClientRects().length ) { + val = elem.getBoundingClientRect()[ name ]; + } + } else { + // In IE8 gBCR doesn't report width & height; we need to fall back to offset*. + val = name === "width" ? elem.offsetWidth : elem.offsetHeight; + } + // Support: IE11 only // In IE 11 fullscreen elements inside of an iframe have // 100x too small dimensions (gh-1764). if ( document.msFullscreenElement && window.top !== window ) { - // Support: IE11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - if ( elem.getClientRects().length ) { - val = Math.round( elem.getBoundingClientRect()[ name ] * 100 ); - } + val *= 100; } // some non-html elements return undefined for offsetWidth, so check for null/undefined @@ -320,7 +327,13 @@ jQuery.each([ "height", "width" ], function( i, name ) { // certain elements can have dimension info if we invisibly show them // however, it must have a current display style that would benefit from this return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - elem.offsetWidth === 0 ? + // 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. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); }) : diff --git a/src/css/support.js b/src/css/support.js index 17848d535..854fa6b36 100644 --- a/src/css/support.js +++ b/src/css/support.js @@ -4,8 +4,8 @@ define([ ], function( jQuery, support ) { (function() { - var div, container, style, a, pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, - reliableHiddenOffsetsVal, reliableMarginRightVal; + var div, container, style, a, pixelPositionVal, boxSizingReliableVal, gBCRDimensionsVal, + pixelMarginRightVal, reliableHiddenOffsetsVal, reliableMarginRightVal; // Setup div = document.createElement( "div" ); @@ -55,6 +55,13 @@ define([ return boxSizingReliableVal; }, + gBCRDimensions: function() { + if ( pixelPositionVal == null ) { + computeStyleTests(); + } + return gBCRDimensionsVal; + }, + pixelMarginRight: function() { // Support: Android 4.0-4.3 if ( pixelPositionVal == null ) { @@ -105,6 +112,10 @@ define([ pixelPositionVal = boxSizingReliableVal = false; pixelMarginRightVal = reliableMarginRightVal = true; + // Support: IE<9 + // In IE8 gBCR doesn't report width & height. + gBCRDimensionsVal = !!div.getBoundingClientRect().width; + // Check for getComputedStyle so that this code is not run in IE<9. if ( window.getComputedStyle ) { divStyle = window.getComputedStyle( div ); diff --git a/test/unit/css.js b/test/unit/css.js index 34620d05e..9c3a186d0 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -904,6 +904,52 @@ testIframeWithCallback( "css('width') should work correctly before document read } ); +( function() { + var supportsFractionalGBCR, + qunitFixture = document.getElementById( "qunit-fixture" ), + div = document.createElement( "div" ); + div.style.width = "3.3px"; + qunitFixture.appendChild( div ); + supportsFractionalGBCR = jQuery.support.gBCRDimensions() && + div.getBoundingClientRect().width.toFixed(1) === "3.3"; + qunitFixture.removeChild( div ); + + test( "css('width') and css('height') should return fractional values for nodes in the document", function() { + if ( !supportsFractionalGBCR ) { + expect( 1 ); + ok( true, "This browser doesn't support fractional values in getBoundingClientRect()" ); + return; + } + + expect( 2 ); + + var el = jQuery( "
" ).appendTo( "#qunit-fixture" ); + jQuery( "" ).appendTo( "#qunit-fixture" ); + + equal( Number( el.css( "width" ).replace( /px$/, "" ) ).toFixed( 1 ), "33.3", + "css('width') should return fractional values" ); + equal( Number( el.css( "height" ).replace( /px$/, "" ) ).toFixed( 1 ), "88.8", + "css('height') should return fractional values" ); + } ); + + test( "css('width') and css('height') should return fractional values for disconnected nodes", function() { + if ( !supportsFractionalGBCR ) { + expect( 1 ); + ok( true, "This browser doesn't support fractional values in getBoundingClientRect()" ); + return; + } + + expect( 2 ); + + var el = jQuery( "
" ); + + equal( Number( el.css( "width" ).replace( /px$/, "" ) ).toFixed( 1 ), "33.3", + "css('width') should return fractional values" ); + equal( Number( el.css( "height" ).replace( /px$/, "" ) ).toFixed( 1 ), "88.8", + "css('height') should return fractional values" ); + } ); +} )(); + test("certain css values of 'normal' should be convertable to a number, see #8627", function() { expect ( 3 ); diff --git a/test/unit/dimensions.js b/test/unit/dimensions.js index c03b96c8b..f4a35981b 100644 --- a/test/unit/dimensions.js +++ b/test/unit/dimensions.js @@ -258,10 +258,12 @@ test("child of a hidden elem (or unconnected node) has accurate inner/outer/Widt equal( $divChild.outerWidth(), $divNormal.outerWidth(), "child of a hidden element outerWidth() is wrong see #9441" ); equal( $divChild.outerWidth(true), $divNormal.outerWidth( true ), "child of a hidden element outerWidth( true ) is wrong see #9300" ); - equal( $divChild.height(), $divNormal.height(), "child of a hidden element height() is wrong see #9441" ); - equal( $divChild.innerHeight(), $divNormal.innerHeight(), "child of a hidden element innerHeight() is wrong see #9441" ); - equal( $divChild.outerHeight(), $divNormal.outerHeight(), "child of a hidden element outerHeight() is wrong see #9441" ); - equal( $divChild.outerHeight(true), $divNormal.outerHeight( true ), "child of a hidden element outerHeight( true ) is wrong see #9300" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divChild.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "child of a hidden element height() is wrong see #9441" ); + equal( $divChild.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "child of a hidden element innerHeight() is wrong see #9441" ); + equal( $divChild.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "child of a hidden element outerHeight() is wrong see #9441" ); + equal( $divChild.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "child of a hidden element outerHeight( true ) is wrong see #9300" ); // tests that child div of an unconnected div works the same as a normal div equal( $divUnconnected.width(), $divNormal.width(), "unconnected element width() is wrong see #9441" ); @@ -269,10 +271,12 @@ test("child of a hidden elem (or unconnected node) has accurate inner/outer/Widt equal( $divUnconnected.outerWidth(), $divNormal.outerWidth(), "unconnected element outerWidth() is wrong see #9441" ); equal( $divUnconnected.outerWidth(true), $divNormal.outerWidth( true ), "unconnected element outerWidth( true ) is wrong see #9300" ); - equal( $divUnconnected.height(), $divNormal.height(), "unconnected element height() is wrong see #9441" ); - equal( $divUnconnected.innerHeight(), $divNormal.innerHeight(), "unconnected element innerHeight() is wrong see #9441" ); - equal( $divUnconnected.outerHeight(), $divNormal.outerHeight(), "unconnected element outerHeight() is wrong see #9441" ); - equal( $divUnconnected.outerHeight(true), $divNormal.outerHeight( true ), "unconnected element outerHeight( true ) is wrong see #9300" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divUnconnected.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "unconnected element height() is wrong see #9441" ); + equal( $divUnconnected.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "unconnected element innerHeight() is wrong see #9441" ); + equal( $divUnconnected.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "unconnected element outerHeight() is wrong see #9441" ); + equal( $divUnconnected.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "unconnected element outerHeight( true ) is wrong see #9300" ); // teardown html $divHiddenParent.remove(); @@ -329,10 +333,12 @@ test("box-sizing:border-box child of a hidden elem (or unconnected node) has acc equal( $divChild.outerWidth(), $divNormal.outerWidth(), "child of a hidden element outerWidth() is wrong see #10413" ); equal( $divChild.outerWidth(true), $divNormal.outerWidth( true ), "child of a hidden element outerWidth( true ) is wrong see #10413" ); - equal( $divChild.height(), $divNormal.height(), "child of a hidden element height() is wrong see #10413" ); - equal( $divChild.innerHeight(), $divNormal.innerHeight(), "child of a hidden element innerHeight() is wrong see #10413" ); - equal( $divChild.outerHeight(), $divNormal.outerHeight(), "child of a hidden element outerHeight() is wrong see #10413" ); - equal( $divChild.outerHeight(true), $divNormal.outerHeight( true ), "child of a hidden element outerHeight( true ) is wrong see #10413" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divChild.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "child of a hidden element height() is wrong see #10413" ); + equal( $divChild.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "child of a hidden element innerHeight() is wrong see #10413" ); + equal( $divChild.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "child of a hidden element outerHeight() is wrong see #10413" ); + equal( $divChild.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "child of a hidden element outerHeight( true ) is wrong see #10413" ); // tests that child div of an unconnected div works the same as a normal div equal( $divUnconnected.width(), $divNormal.width(), "unconnected element width() is wrong see #10413" ); @@ -340,10 +346,12 @@ test("box-sizing:border-box child of a hidden elem (or unconnected node) has acc equal( $divUnconnected.outerWidth(), $divNormal.outerWidth(), "unconnected element outerWidth() is wrong see #10413" ); equal( $divUnconnected.outerWidth(true), $divNormal.outerWidth( true ), "unconnected element outerWidth( true ) is wrong see #10413" ); - equal( $divUnconnected.height(), $divNormal.height(), "unconnected element height() is wrong see #10413" ); - equal( $divUnconnected.innerHeight(), $divNormal.innerHeight(), "unconnected element innerHeight() is wrong see #10413" ); - equal( $divUnconnected.outerHeight(), $divNormal.outerHeight(), "unconnected element outerHeight() is wrong see #10413" ); - equal( $divUnconnected.outerHeight(true), $divNormal.outerHeight( true ), "unconnected element outerHeight( true ) is wrong see #10413" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divUnconnected.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "unconnected element height() is wrong see #10413" ); + equal( $divUnconnected.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "unconnected element innerHeight() is wrong see #10413" ); + equal( $divUnconnected.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "unconnected element outerHeight() is wrong see #10413" ); + equal( $divUnconnected.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "unconnected element outerHeight( true ) is wrong see #10413" ); // teardown html $divHiddenParent.remove(); diff --git a/test/unit/support.js b/test/unit/support.js index 07478e014..b5865b2bd 100644 --- a/test/unit/support.js +++ b/test/unit/support.js @@ -82,6 +82,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -114,6 +115,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": true, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -146,6 +148,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": true, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -178,6 +181,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": false, "deleteExpando": false, "focusin": true, + "gBCRDimensions": false, "html5Clone": false, "htmlSerialize": false, "input": false, @@ -212,6 +216,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -244,6 +249,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -276,6 +282,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -308,6 +315,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -340,6 +348,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -372,6 +381,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -404,6 +414,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true, @@ -436,6 +447,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "cssFloat": true, "deleteExpando": true, "focusin": false, + "gBCRDimensions": true, "html5Clone": true, "htmlSerialize": true, "input": true,