CSS: Restore cascade-override behavior in .show

Fixes gh-2654
Fixes gh-2308
Close gh-2810
Ref 86419b10bf
This commit is contained in:
Richard Gibson 2016-01-11 02:26:55 -05:00 committed by Timmy Willison
parent a268f5225c
commit dba93f79c4
5 changed files with 388 additions and 113 deletions

View File

@ -4,6 +4,31 @@ define( [
"../css/var/isHidden"
], function( jQuery, dataPriv, isHidden ) {
var defaultDisplayMap = {};
function getDefaultDisplay( elem ) {
var temp,
doc = elem.ownerDocument,
nodeName = elem.nodeName,
display = defaultDisplayMap[ nodeName ];
if ( display ) {
return display;
}
temp = doc.body.appendChild( doc.createElement( nodeName ) ),
display = jQuery.css( temp, "display" );
temp.parentNode.removeChild( temp );
if ( display === "none" ) {
display = "block";
}
defaultDisplayMap[ nodeName ] = display;
return display;
}
function showHide( elements, show ) {
var display, elem,
values = [],
@ -19,23 +44,30 @@ function showHide( elements, show ) {
display = elem.style.display;
if ( show ) {
if ( display === "none" ) {
// Restore a pre-hide() value if we have one
values[ index ] = dataPriv.get( elem, "display" ) || "";
// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
// check is required in this first loop unless we have a nonempty display value (either
// inline or about-to-be-restored)
if ( display === "none" ) {
values[ index ] = dataPriv.get( elem, "display" ) || null;
if ( !values[ index ] ) {
elem.style.display = "";
}
}
if ( elem.style.display === "" && jQuery.css( elem, "display" ) === "none" ) {
values[ index ] = getDefaultDisplay( elem );
}
} else {
if ( display !== "none" ) {
values[ index ] = "none";
// Remember the value we're replacing
// Remember what we're overwriting
dataPriv.set( elem, "display", display );
}
}
}
// Set the display of the elements in a second loop
// to avoid the constant reflow
// Set the display of the elements in a second loop to avoid constant reflow
for ( index = 0; index < length; index++ ) {
if ( values[ index ] != null ) {
elements[ index ].style.display = values[ index ];

13
src/effects.js vendored
View File

@ -154,9 +154,16 @@ function defaultPrefilter( elem, props, opts ) {
}
display = jQuery.css( elem, "display" );
if ( display === "none" ) {
display = restoreDisplay || swap( elem, { "display": "" }, function() {
return jQuery.css( elem, "display" );
} );
if ( restoreDisplay ) {
display = restoreDisplay;
} else {
// Get nonempty value(s) by temporarily forcing visibility
showHide( [ elem ], true );
restoreDisplay = elem.style.display || restoreDisplay;
display = jQuery.css( elem, "display" );
showHide( [ elem ] );
}
}
// Animate inline elements as inline-block

View File

@ -20,10 +20,6 @@ div#fx-tests div.overflow {
overflow: visible;
}
div.inline {
display: inline;
}
div.autoheight {
height: auto;
}
@ -68,11 +64,6 @@ div.noopacity {
opacity: 0;
}
div.hidden,
span.hidden {
display: none;
}
div#fx-tests div.widewidth {
background-repeat: repeat-x;
}
@ -134,3 +125,8 @@ section { background:#f0f; display:block; }
#span-14824 { display: block; }
#display { display: list-item !important; }
.block { display: block; }
.inline { display: inline; }
.list-item { display: list-item; }
.hidden, .none { display: none; }

View File

@ -474,26 +474,7 @@ QUnit.test( "css(Object) where values are Functions with incoming values", funct
// .show(), .hide(), can be excluded from the build
if ( jQuery.fn.show && jQuery.fn.hide ) {
QUnit.test( "show(); hide()", function( assert ) {
assert.expect( 4 );
var hiddendiv, div;
hiddendiv = jQuery( "div.hidden" );
hiddendiv.hide();
assert.equal( hiddendiv.css( "display" ), "none", "Cascade-hidden div after hide()" );
hiddendiv.show();
assert.equal( hiddendiv.css( "display" ), "none", "Show does not trump CSS cascade" );
div = jQuery( "<div>" ).hide();
assert.equal( div.css( "display" ), "none", "Detached div hidden" );
div.appendTo( "#qunit-fixture" ).show();
assert.equal( div.css( "display" ), "block", "Initially-detached div after show()" );
} );
QUnit.test( "show();", function( assert ) {
QUnit.test( "show()", function( assert ) {
assert.expect( 18 );
@ -557,11 +538,20 @@ QUnit.test( "show();", function( assert ) {
jQuery( "<div>test</div> text <span>test</span>" ).hide().remove();
} );
QUnit.test( "show() resolves correct default display for detached nodes", function( assert ) {
assert.expect( 16 );
QUnit.test( "show/hide detached nodes", function( assert ) {
assert.expect( 19 );
var div, span, tr;
div = jQuery( "<div>" ).hide();
assert.equal( div.css( "display" ), "none", "hide() updates inline style of a detached div" );
div.appendTo( "#qunit-fixture" );
assert.equal( div.css( "display" ), "none",
"A hidden-while-detached div is hidden after attachment" );
div.show();
assert.equal( div.css( "display" ), "block",
"A hidden-while-detached div can be shown after attachment" );
div = jQuery( "<div class='hidden'>" );
div.show().appendTo( "#qunit-fixture" );
assert.equal( div.css( "display" ), "none",
@ -664,6 +654,247 @@ QUnit.test( "show() after hide() should always set display to initial value (#14
assert.equal( div.css( "display" ), "list-item", "should get last set display value" );
} );
QUnit.test( "show/hide 3.0, default display", function( assert ) {
assert.expect( 36 );
var i,
$elems = jQuery( "<div/>" )
.appendTo( "#qunit-fixture" )
.html( "<div data-expected-display='block'/>" +
"<span data-expected-display='inline'/>" +
"<ul><li data-expected-display='list-item'/></ul>" )
.find( "[data-expected-display]" );
$elems.each( function() {
var $elem = jQuery( this ),
name = this.nodeName,
expected = this.getAttribute( "data-expected-display" ),
sequence = [];
if ( this.className ) {
name += "." + this.className;
}
if ( this.getAttribute( "style" ) ) {
name += "[style='" + this.getAttribute( "style" ) + "']";
}
name += " ";
for ( i = 0; i < 3; i++ ) {
sequence.push( ".show()" );
$elem.show();
assert.equal( $elem.css( "display" ), expected,
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "", name + sequence.join( "" ) + " inline" );
sequence.push( ".hide()" );
$elem.hide();
assert.equal( $elem.css( "display" ), "none",
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "none", name + sequence.join( "" ) + " inline" );
}
} );
} );
QUnit.test( "show/hide 3.0, default body display", function( assert ) {
assert.expect( 2 );
var hideBody = supportjQuery( "<style>body{display:none}</style>" ).appendTo( document.head ),
body = jQuery( document.body );
assert.equal( body.css( "display" ), "none", "Correct initial display" );
body.show();
assert.equal( body.css( "display" ), "block", "Correct display after .show()" );
hideBody.remove();
} );
QUnit.test( "show/hide 3.0, cascade display", function( assert ) {
assert.expect( 36 );
var i,
$elems = jQuery( "<div/>" )
.appendTo( "#qunit-fixture" )
.html( "<span class='block'/><div class='inline'/><div class='list-item'/>" )
.children();
$elems.each( function() {
var $elem = jQuery( this ),
name = this.nodeName,
sequence = [];
if ( this.className ) {
name += "." + this.className;
}
if ( this.getAttribute( "style" ) ) {
name += "[style='" + this.getAttribute( "style" ) + "']";
}
name += " ";
for ( i = 0; i < 3; i++ ) {
sequence.push( ".show()" );
$elem.show();
assert.equal( $elem.css( "display" ), this.className,
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "", name + sequence.join( "" ) + " inline" );
sequence.push( ".hide()" );
$elem.hide();
assert.equal( $elem.css( "display" ), "none",
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "none", name + sequence.join( "" ) + " inline" );
}
} );
} );
QUnit.test( "show/hide 3.0, inline display", function( assert ) {
assert.expect( 96 );
var i,
$elems = jQuery( "<div/>" )
.appendTo( "#qunit-fixture" )
.html( "<span data-expected-display='block' style='display:block'/>" +
"<span class='list-item' data-expected-display='block' style='display:block'/>" +
"<div data-expected-display='inline' style='display:inline'/>" +
"<div class='list-item' data-expected-display='inline' style='display:inline'/>" +
"<ul>" +
"<li data-expected-display='block' style='display:block'/>" +
"<li class='inline' data-expected-display='block' style='display:block'/>" +
"<li data-expected-display='inline' style='display:inline'/>" +
"<li class='block' data-expected-display='inline' style='display:inline'/>" +
"</ul>" )
.find( "[data-expected-display]" );
$elems.each( function() {
var $elem = jQuery( this ),
name = this.nodeName,
expected = this.getAttribute( "data-expected-display" ),
sequence = [];
if ( this.className ) {
name += "." + this.className;
}
if ( this.getAttribute( "style" ) ) {
name += "[style='" + this.getAttribute( "style" ) + "']";
}
name += " ";
for ( i = 0; i < 3; i++ ) {
sequence.push( ".show()" );
$elem.show();
assert.equal( $elem.css( "display" ), expected,
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, expected, name + sequence.join( "" ) + " inline" );
sequence.push( ".hide()" );
$elem.hide();
assert.equal( $elem.css( "display" ), "none",
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "none", name + sequence.join( "" ) + " inline" );
}
} );
} );
QUnit.test( "show/hide 3.0, cascade hidden", function( assert ) {
assert.expect( 72 );
var i,
$elems = jQuery( "<div/>" )
.appendTo( "#qunit-fixture" )
.html( "<div class='hidden' data-expected-display='block'/>" +
"<div class='hidden' data-expected-display='block' style='display:none'/>" +
"<span class='hidden' data-expected-display='inline'/>" +
"<span class='hidden' data-expected-display='inline' style='display:none'/>" +
"<ul>" +
"<li class='hidden' data-expected-display='list-item'/>" +
"<li class='hidden' data-expected-display='list-item' style='display:none'/>" +
"</ul>" )
.find( "[data-expected-display]" );
$elems.each( function() {
var $elem = jQuery( this ),
name = this.nodeName,
expected = this.getAttribute( "data-expected-display" ),
sequence = [];
if ( this.className ) {
name += "." + this.className;
}
if ( this.getAttribute( "style" ) ) {
name += "[style='" + this.getAttribute( "style" ) + "']";
}
name += " ";
for ( i = 0; i < 3; i++ ) {
sequence.push( ".hide()" );
$elem.hide();
assert.equal( $elem.css( "display" ), "none",
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "none", name + sequence.join( "" ) + " inline" );
sequence.push( ".show()" );
$elem.show();
assert.equal( $elem.css( "display" ), expected,
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, expected, name + sequence.join( "" ) + " inline" );
}
} );
} );
QUnit.test( "show/hide 3.0, inline hidden", function( assert ) {
assert.expect( 84 );
var i,
$elems = jQuery( "<div/>" )
.appendTo( "#qunit-fixture" )
.html( "<span data-expected-display='inline' style='display:none'/>" +
"<span class='list-item' data-expected-display='list-item' style='display:none'/>" +
"<div data-expected-display='block' style='display:none'/>" +
"<div class='list-item' data-expected-display='list-item' style='display:none'/>" +
"<ul>" +
"<li data-expected-display='list-item' style='display:none'/>" +
"<li class='block' data-expected-display='block' style='display:none'/>" +
"<li class='inline' data-expected-display='inline' style='display:none'/>" +
"</ul>" )
.find( "[data-expected-display]" );
$elems.each( function() {
var $elem = jQuery( this ),
name = this.nodeName,
expected = this.getAttribute( "data-expected-display" ),
sequence = [];
if ( this.className ) {
name += "." + this.className;
}
if ( this.getAttribute( "style" ) ) {
name += "[style='" + this.getAttribute( "style" ) + "']";
}
name += " ";
for ( i = 0; i < 3; i++ ) {
sequence.push( ".hide()" );
$elem.hide();
assert.equal( $elem.css( "display" ), "none",
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "none", name + sequence.join( "" ) + " inline" );
sequence.push( ".show()" );
$elem.show();
assert.equal( $elem.css( "display" ), expected,
name + sequence.join( "" ) + " computed" );
assert.equal( this.style.display, "", name + sequence.join( "" ) + " inline" );
}
} );
} );
}
QUnit[ jQuery.find.compile && jQuery.fn.toggle ? "test" : "skip" ]( "toggle()", function( assert ) {

155
test/unit/effects.js vendored
View File

@ -5,7 +5,11 @@ if ( !jQuery.fx ) {
return;
}
var oldRaf = window.requestAnimationFrame;
var oldRaf = window.requestAnimationFrame,
hideOptions = {
inline: function() { jQuery.style( this, "display", "none" ); },
cascade: function() { this.className = "hidden"; }
};
QUnit.module( "effects", {
setup: function() {
@ -131,98 +135,103 @@ QUnit.test( "show()", function( assert ) {
jQuery( "<div>test</div> text <span>test</span>" ).hide().remove();
} );
QUnit.test( "show(Number) - other displays", function( assert ) {
assert.expect( 30 );
supportjQuery.each( hideOptions, function( type, setup ) {
QUnit.test( "show(Number) - " + type + " hidden", function( assert ) {
assert.expect( 30 );
jQuery(
"<div id='show-tests'>" +
"<div><p><a href='#'></a></p><code></code><pre></pre><span></span></div>" +
"<table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody></table>" +
"<ul><li></li></ul></div>" +
"<table id='test-table'></table>"
).appendTo( "#qunit-fixture" ).find( "*" ).css( "display", "none" );
jQuery(
"<div id='show-tests'>" +
"<div><p><a href='#'></a></p><code></code><pre></pre><span></span></div>" +
"<table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody>" +
"</table>" +
"<ul><li></li></ul></div>" +
"<table id='test-table'></table>"
).appendTo( "#qunit-fixture" ).find( "*" ).each( setup );
var test,
old = jQuery( "#test-table" ).show().css( "display" ) !== "table";
var test,
old = jQuery( "#test-table" ).show().css( "display" ) !== "table";
jQuery( "#test-table" ).remove();
jQuery( "#test-table" ).remove();
// Note: inline elements are expected to be inline-block
// because we're showing width/height
// Can't animate width/height inline
// See #14344
test = {
"div": "block",
"p": "block",
"a": "inline",
"code": "inline",
"pre": "block",
"span": "inline",
"table": old ? "block" : "table",
"thead": old ? "block" : "table-header-group",
"tbody": old ? "block" : "table-row-group",
"tr": old ? "block" : "table-row",
"th": old ? "block" : "table-cell",
"td": old ? "block" : "table-cell",
"ul": "block",
"li": old ? "block" : "list-item"
};
// Note: inline elements are expected to be inline-block
// because we're showing width/height
// Can't animate width/height inline
// See #14344
test = {
"div": "block",
"p": "block",
"a": "inline",
"code": "inline",
"pre": "block",
"span": "inline",
"table": old ? "block" : "table",
"thead": old ? "block" : "table-header-group",
"tbody": old ? "block" : "table-row-group",
"tr": old ? "block" : "table-row",
"th": old ? "block" : "table-cell",
"td": old ? "block" : "table-cell",
"ul": "block",
"li": old ? "block" : "list-item"
};
jQuery.each( test, function( selector ) {
jQuery( selector, "#show-tests" ).show( 100 );
} );
this.clock.tick( 50 );
jQuery.each( test, function( selector, expected ) {
jQuery( selector, "#show-tests" ).each( function() {
assert.equal(
jQuery( this ).css( "display" ),
expected === "inline" ? "inline-block" : expected,
"Correct display type during animation for " + selector
);
jQuery.each( test, function( selector ) {
jQuery( selector, "#show-tests" ).show( 100 );
} );
} );
this.clock.tick( 50 );
jQuery.each( test, function( selector, expected ) {
jQuery( selector, "#show-tests" ).each( function() {
assert.equal( jQuery( this ).css( "display" ), expected,
"Correct display type after animation for " + selector );
this.clock.tick( 50 );
jQuery.each( test, function( selector, expected ) {
jQuery( selector, "#show-tests" ).each( function() {
assert.equal(
jQuery( this ).css( "display" ),
expected === "inline" ? "inline-block" : expected,
"Correct display type during animation for " + selector
);
} );
} );
this.clock.tick( 50 );
jQuery.each( test, function( selector, expected ) {
jQuery( selector, "#show-tests" ).each( function() {
assert.equal( jQuery( this ).css( "display" ), expected,
"Correct display type after animation for " + selector );
} );
} );
} );
jQuery( "#show-tests" ).remove();
jQuery( "#show-tests" ).remove();
} );
} );
// Supports #7397
QUnit.test( "Persist correct display value", function( assert ) {
assert.expect( 3 );
supportjQuery.each( hideOptions, function( type, setup ) {
QUnit.test( "Persist correct display value - " + type + " hidden", function( assert ) {
assert.expect( 3 );
jQuery( "<div id='show-tests'><span style='position:absolute;'>foo</span></div>" )
.appendTo( "#qunit-fixture" ).find( "*" ).css( "display", "none" );
jQuery( "<div id='show-tests'><span style='position:absolute;'>foo</span></div>" )
.appendTo( "#qunit-fixture" ).find( "*" ).each( setup );
var $span = jQuery( "#show-tests span" ),
displayNone = $span.css( "display" ),
display = "",
clock = this.clock;
var $span = jQuery( "#show-tests span" ),
displayNone = $span.css( "display" ),
display = "",
clock = this.clock;
$span.show();
$span.show();
display = $span.css( "display" );
display = $span.css( "display" );
$span.hide();
$span.hide();
$span.fadeIn( 100, function() {
assert.equal( $span.css( "display" ), display, "Expecting display: " + display );
$span.fadeOut( 100, function() {
assert.equal( $span.css( "display" ), displayNone, "Expecting display: " + displayNone );
$span.fadeIn( 100, function() {
assert.equal( $span.css( "display" ), display, "Expecting display: " + display );
$span.fadeIn( 100, function() {
assert.equal( $span.css( "display" ), display, "Expecting display: " + display );
$span.fadeOut( 100, function() {
assert.equal( $span.css( "display" ), displayNone, "Expecting display: " + displayNone );
$span.fadeIn( 100, function() {
assert.equal( $span.css( "display" ), display, "Expecting display: " + display );
} );
} );
} );
clock.tick( 300 );
assert.expectJqData( this, $span, "olddisplay" );
} );
clock.tick( 300 );
assert.expectJqData( this, $span, "olddisplay" );
} );
QUnit.test( "animate(Hash, Object, Function)", function( assert ) {