From 0229b83f7e5bf64edb2888ab349bedcd1a45e7c1 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Tue, 5 Oct 2010 13:23:10 -0500 Subject: [PATCH 1/7] Fix :visible does not work properly when display:none is set directly on an element in IE8. Fixes #4512. --- src/css.js | 9 ++------- src/support.js | 15 ++++++++++++++- test/unit/css.js | 13 +++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/css.js b/src/css.js index 4bf818e5..8751860a 100644 --- a/src/css.js +++ b/src/css.js @@ -280,14 +280,9 @@ function getWH( elem, name, extra ) { if ( jQuery.expr && jQuery.expr.filters ) { jQuery.expr.filters.hidden = function( elem ) { - var width = elem.offsetWidth, height = elem.offsetHeight, - skip = elem.nodeName.toLowerCase() === "tr"; + var width = elem.offsetWidth, height = elem.offsetHeight; - return width === 0 && height === 0 && !skip ? - true : - width > 0 && height > 0 && !skip ? - false : - (elem.style.display || jQuery.css( elem, "display" )) === "none"; + return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none"); }; jQuery.expr.filters.visible = function( elem ) { diff --git a/src/support.js b/src/support.js index d35dbed2..299fbd27 100644 --- a/src/support.js +++ b/src/support.js @@ -65,7 +65,8 @@ checkClone: false, scriptEval: false, noCloneEvent: true, - boxModel: null + boxModel: null, + reliableHiddenOffsets: true }; // Make sure that the options inside disabled selects aren't marked as disabled @@ -117,6 +118,18 @@ document.body.appendChild( div ); jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = '
t
'; + jQuery.support.reliableHiddenOffsets = div.getElementsByTagName('td')[0].offsetHeight === 0; + div.innerHTML = ''; + document.body.removeChild( div ).style.display = 'none'; div = null; }); diff --git a/test/unit/css.js b/test/unit/css.js index 468f7638..cfffb783 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -256,3 +256,16 @@ test("jQuery.css(elem, 'height') doesn't clear radio buttons (bug #1095)", funct ok( !! jQuery(":checkbox:first", $checkedtest).attr("checked"), "Check first checkbox still checked." ); ok( ! jQuery(":checkbox:last", $checkedtest).attr("checked"), "Check last checkbox still NOT checked." ); }); + +test(":visible selector works properly on table elements (bug #4512)", function () { + expect(1); + + jQuery('#table').html('cellcell'); + equals(jQuery('#table td:visible').length, 1, "hidden cell is not perceived as visible"); +}); + +test(":visible selector works properly on children with a hidden parent (bug #4512)", function () { + expect(1); + jQuery('#table').css('display', 'none').html('cellcell'); + equals(jQuery('#table td:visible').length, 0, "hidden cell children not perceived as visible"); +}); \ No newline at end of file From 0cdcef5b364fa3c89d7f27935e77b453f4a56efd Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Tue, 5 Oct 2010 13:28:43 -0500 Subject: [PATCH 2/7] Update $.fn.animate to change display mode only when necessary (inline, non-floated elements), and to use a more proper display mode for those elements. Fixes #2185. --- src/effects.js | 104 +++++++++++++++++++++------------------- src/support.js | 12 +++++ test/data/testsuite.css | 2 + test/index.html | 4 +- test/unit/css.js | 2 +- test/unit/effects.js | 75 ++++++++++++++++++++++++++++- 6 files changed, 146 insertions(+), 53 deletions(-) diff --git a/src/effects.js b/src/effects.js index 56141bb2..3257ac39 100644 --- a/src/effects.js +++ b/src/effects.js @@ -13,45 +13,41 @@ var elemdisplay = {}, [ "opacity" ] ]; +function defaultDisplay(nodeName) { + if ( !elemdisplay[ nodeName ] ) { + var elem = jQuery("<" + nodeName + ">").appendTo("body"), + display = elem.css("display"); + + elem.remove(); + + if ( display === "none" || display === "" ) { + display = "block"; + } + + elemdisplay[ nodeName ] = display; + } + + return elemdisplay[ nodeName ]; +} + jQuery.fn.extend({ show: function( speed, easing, callback ) { - if ( speed || speed === 0) { + if ( speed || speed === 0 ) { return this.animate( genFx("show", 3), speed, easing, callback); - } else { - for ( var i = 0, l = this.length; i < l; i++ ) { - var old = jQuery.data(this[i], "olddisplay"); - - this[i].style.display = old || ""; - - if ( jQuery.css( this[i], "display" ) === "none" ) { - var nodeName = this[i].nodeName, display; - - if ( elemdisplay[ nodeName ] ) { - display = elemdisplay[ nodeName ]; - - } else { - var elem = jQuery("<" + nodeName + ">").appendTo("body"); - - display = elem.css("display"); - - if ( display === "none" ) { - display = "block"; - } - - elem.remove(); - - elemdisplay[ nodeName ] = display; - } - - jQuery.data(this[i], "olddisplay", display); + for ( var i = 0, j = this.length; i < j; i++ ) { + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( jQuery.css( this[i], "display" ) === "none" && this[i].style.display !== "none" ) { + jQuery.data(this[i], "olddisplay", defaultDisplay(this[i].nodeName)); } } // Set the display of the elements in a second loop // to avoid the constant reflow - for ( var j = 0, k = this.length; j < k; j++ ) { - this[j].style.display = jQuery.data(this[j], "olddisplay") || ""; + for ( i = 0, j = this.length; i < j; i++ ) { + this[i].style.display = jQuery.data(this[i], "olddisplay") || ""; } return this; @@ -115,6 +111,9 @@ jQuery.fn.extend({ } return this[ optall.queue === false ? "each" : "queue" ](function() { + // XXX ‘this’ does not always have a nodeName when running the + // test suite + var opt = jQuery.extend({}, optall), p, hidden = this.nodeType === 1 && jQuery(this).is(":hidden"), self = this; @@ -132,12 +131,32 @@ jQuery.fn.extend({ return opt.complete.call(this); } - if ( ( p === "height" || p === "width" ) && this.style ) { - // Store display property - opt.display = this.style.display; - + if ( ( p === "height" || p === "width" ) ) { // Make sure that nothing sneaks out opt.overflow = this.style.overflow; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height + // animated + if ( jQuery.curCSS( this, "display" ) === "inline" && + jQuery.curCSS( this, "float" ) === "none" ) { + if ( !jQuery.support.inlineBlockNeedsLayout ) { + this.style.display = "inline-block"; + } else { + var display = defaultDisplay(this.nodeName); + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( display === "inline" ) { + this.style.display = "inline-block"; + } + else { + this.style.display = "inline"; + jQuery.data( this, "oldzoom", this.style.zoom ); + this.style.zoom = 1; + } + } + } } if ( jQuery.isArray( prop[p] ) ) { @@ -303,11 +322,6 @@ jQuery.fx.prototype = { } (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this ); - - // Set display property to block for height/width animations - if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) { - this.elem.style.display = "block"; - } }, // Get the current size @@ -384,17 +398,9 @@ jQuery.fx.prototype = { } if ( done ) { - if ( this.options.display != null ) { - // Reset the overflow + // Reset the overflow + if ( this.options.overflow != null ) { this.elem.style.overflow = this.options.overflow; - - // Reset the display - var old = jQuery.data(this.elem, "olddisplay"); - this.elem.style.display = old ? old : this.options.display; - - if ( jQuery.css( this.elem, "display" ) === "none" ) { - this.elem.style.display = "block"; - } } // Hide the element if the "hide" operation was done diff --git a/src/support.js b/src/support.js index 299fbd27..878db1f0 100644 --- a/src/support.js +++ b/src/support.js @@ -66,6 +66,7 @@ scriptEval: false, noCloneEvent: true, boxModel: null, + inlineBlockNeedsLayout: false, reliableHiddenOffsets: true }; @@ -119,6 +120,17 @@ document.body.appendChild( div ); jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' + // (IE < 8 does this) + if ( 'zoom' in div.style ) { + div.style.display = 'inline'; + + // Layout is necessary to trigger this “feature” + div.style.zoom = 1; + jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2; + } + // Check if table cells still have offsetWidth/Height when they are set // to display:none and there are still other visible table cells in a // table row; if so, offsetWidth/Height are not reliable for use when diff --git a/test/data/testsuite.css b/test/data/testsuite.css index 18468920..cffaaa46 100644 --- a/test/data/testsuite.css +++ b/test/data/testsuite.css @@ -102,6 +102,8 @@ div.chain.test div { background: green; } div.chain.out { background: green; } div.chain.out div { background: red; display: none; } +/* tests to ensure jQuery can determine the native display mode of elements + that have been set as display: none in stylesheets */ div#show-tests * { display: none; } #nothiddendiv { font-size: 16px; } diff --git a/test/index.html b/test/index.html index d858114b..262bd9a9 100644 --- a/test/index.html +++ b/test/index.html @@ -55,8 +55,8 @@ -