Skip to content

Commit 755e7cc

Browse files
committed
CSS: Toggle detached elements as visible unless they have display: none
Fixes gh-2863 Closes gh-3037
1 parent ce6c83f commit 755e7cc

File tree

4 files changed

+61
-14
lines changed

4 files changed

+61
-14
lines changed

src/css/showHide.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,7 @@ function showHide( elements, show ) {
5454
elem.style.display = "";
5555
}
5656
}
57-
if ( elem.style.display === "" && jQuery.css( elem, "display" ) === "none" &&
58-
59-
// Support: Firefox 43+
60-
// Don't set inline display on disconnected elements with computed display: none
61-
jQuery.contains( elem.ownerDocument, elem ) ) {
62-
57+
if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
6358
values[ index ] = getDefaultDisplay( elem );
6459
}
6560
} else {

src/css/var/isHiddenWithinTree.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,29 @@ define( [
55
// css is assumed
66
], function( jQuery ) {
77

8-
// This function differs from the :hidden selector
9-
// in that it intentionally ignores hidden ancestors (gh-2404)
8+
// isHiddenWithinTree reports if an element has a non-"none" display style (inline and/or
9+
// through the CSS cascade), which is useful in deciding whether or not to make it visible.
10+
// It differs from the :hidden selector (jQuery.expr.pseudos.hidden) in two important ways:
11+
// * A hidden ancestor does not force an element to be classified as hidden.
12+
// * Being disconnected from the document does not force an element to be classified as hidden.
13+
// These differences improve the behavior of .toggle() et al. when applied to elements that are
14+
// detached or contained within hidden ancestors (gh-2404, gh-2863).
1015
return function( elem, el ) {
1116

1217
// isHiddenWithinTree might be called from jQuery#filter function;
1318
// in that case, element will be second argument
1419
elem = el || elem;
15-
return jQuery.css( elem, "display" ) === "none" ||
16-
!jQuery.contains( elem.ownerDocument, elem );
20+
21+
// Inline style trumps all
22+
return elem.style.display === "none" ||
23+
elem.style.display === "" &&
24+
25+
// Otherwise, check computed style
26+
// Support: Firefox <=43 - 45
27+
// Disconnected elements can have computed display: none, so first confirm that elem is
28+
// in the document.
29+
jQuery.contains( elem.ownerDocument, elem ) &&
30+
31+
jQuery.css( elem, "display" ) === "none";
1732
};
1833
} );

test/unit/css.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,30 @@ QUnit[ jQuery.find.compile && jQuery.fn.toggle ? "test" : "skip" ]( "toggle()",
926926
jQuery.fn.hide = oldHide;
927927
} );
928928

929+
QUnit[ jQuery.find.compile && jQuery.fn.toggle ? "test" : "skip" ]( "detached toggle()", function( assert ) {
930+
assert.expect( 6 );
931+
var detached = jQuery( "<p><a/><p>" ).find( "*" ).addBack(),
932+
hiddenDetached = jQuery( "<p><a/></p>" ).find( "*" ).addBack().css( "display", "none" ),
933+
cascadeHiddenDetached = jQuery( "<p><a/></p>" ).find( "*" ).addBack().addClass( "hidden" );
934+
935+
detached.toggle();
936+
detached.appendTo( "#qunit-fixture" );
937+
assert.equal( detached[ 0 ].style.display, "none", "detached element" );
938+
assert.equal( detached[ 1 ].style.display, "none", "element in detached tree" );
939+
940+
hiddenDetached.toggle();
941+
hiddenDetached.appendTo( "#qunit-fixture" );
942+
assert.equal( hiddenDetached[ 0 ].style.display, "", "detached, hidden element" );
943+
assert.equal( hiddenDetached[ 1 ].style.display, "", "hidden element in detached tree" );
944+
945+
cascadeHiddenDetached.toggle();
946+
cascadeHiddenDetached.appendTo( "#qunit-fixture" );
947+
assert.equal( cascadeHiddenDetached[ 0 ].style.display, "none",
948+
"detached, cascade-hidden element" );
949+
assert.equal( cascadeHiddenDetached[ 1 ].style.display, "none",
950+
"cascade-hidden element in detached tree" );
951+
} );
952+
929953
QUnit.test( "jQuery.css(elem, 'height') doesn't clear radio buttons (bug #1095)", function( assert ) {
930954
assert.expect( 4 );
931955

test/unit/effects.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,15 +1547,17 @@ QUnit.test( "animate should set display for disconnected nodes", function( asser
15471547
assert.expect( 20 );
15481548

15491549
var env = this,
1550-
methods = {
1551-
toggle: [ 1 ],
1552-
slideToggle: [],
1550+
showMethods = {
15531551
fadeIn: [],
15541552
fadeTo: [ "fast", 0.5 ],
15551553
slideDown: [ "fast" ],
15561554
show: [ 1 ],
15571555
animate: [ { width: "show" } ]
15581556
},
1557+
toggleMethods = {
1558+
toggle: [ 1 ],
1559+
slideToggle: []
1560+
},
15591561
$divEmpty = jQuery( "<div/>" ),
15601562
$divTest = jQuery( "<div>test</div>" ),
15611563
$divNone = jQuery( "<div style='display: none;'/>" ),
@@ -1578,7 +1580,7 @@ QUnit.test( "animate should set display for disconnected nodes", function( asser
15781580

15791581
assert.expectJqData( env, $divNone[ 0 ], "olddisplay" );
15801582

1581-
jQuery.each( methods, function( name, opt ) {
1583+
jQuery.each( showMethods, function( name, opt ) {
15821584
jQuery.fn[ name ].apply( jQuery( "<div/>" ), opt.concat( [ function() {
15831585
assert.strictEqual( jQuery( this ).css( "display" ), nullParentDisplay,
15841586
"." + name + " block with null parentNode" );
@@ -1589,6 +1591,17 @@ QUnit.test( "animate should set display for disconnected nodes", function( asser
15891591
"." + name + " block under fragment" );
15901592
} ] ) );
15911593
} );
1594+
jQuery.each( toggleMethods, function( name, opt ) {
1595+
jQuery.fn[ name ].apply( jQuery( "<div/>" ), opt.concat( [ function() {
1596+
assert.strictEqual( jQuery( this ).css( "display" ), "none",
1597+
"." + name + " block with null parentNode" );
1598+
} ] ) );
1599+
1600+
jQuery.fn[ name ].apply( jQuery( "<div>test</div>" ), opt.concat( [ function() {
1601+
assert.strictEqual( jQuery( this ).css( "display" ), "none",
1602+
"." + name + " block under fragment" );
1603+
} ] ) );
1604+
} );
15921605
clock.tick( 400 );
15931606
} );
15941607

0 commit comments

Comments
 (0)