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" "../css/var/isHidden"
], function( jQuery, dataPriv, 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 ) { function showHide( elements, show ) {
var display, elem, var display, elem,
values = [], values = [],
@ -19,23 +44,30 @@ function showHide( elements, show ) {
display = elem.style.display; display = elem.style.display;
if ( show ) { if ( show ) {
if ( display === "none" ) {
// Restore a pre-hide() value if we have one // Since we force visibility upon cascade-hidden elements, an immediate (and slow)
values[ index ] = dataPriv.get( elem, "display" ) || ""; // 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 { } else {
if ( display !== "none" ) { if ( display !== "none" ) {
values[ index ] = "none"; values[ index ] = "none";
// Remember the value we're replacing // Remember what we're overwriting
dataPriv.set( elem, "display", display ); dataPriv.set( elem, "display", display );
} }
} }
} }
// Set the display of the elements in a second loop // Set the display of the elements in a second loop to avoid constant reflow
// to avoid the constant reflow
for ( index = 0; index < length; index++ ) { for ( index = 0; index < length; index++ ) {
if ( values[ index ] != null ) { if ( values[ index ] != null ) {
elements[ index ].style.display = values[ index ]; 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" ); display = jQuery.css( elem, "display" );
if ( display === "none" ) { if ( display === "none" ) {
display = restoreDisplay || swap( elem, { "display": "" }, function() { if ( restoreDisplay ) {
return jQuery.css( elem, "display" ); 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 // Animate inline elements as inline-block

View File

@ -20,10 +20,6 @@ div#fx-tests div.overflow {
overflow: visible; overflow: visible;
} }
div.inline {
display: inline;
}
div.autoheight { div.autoheight {
height: auto; height: auto;
} }
@ -68,11 +64,6 @@ div.noopacity {
opacity: 0; opacity: 0;
} }
div.hidden,
span.hidden {
display: none;
}
div#fx-tests div.widewidth { div#fx-tests div.widewidth {
background-repeat: repeat-x; background-repeat: repeat-x;
} }
@ -134,3 +125,8 @@ section { background:#f0f; display:block; }
#span-14824 { display: block; } #span-14824 { display: block; }
#display { display: list-item !important; } #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 // .show(), .hide(), can be excluded from the build
if ( jQuery.fn.show && jQuery.fn.hide ) { if ( jQuery.fn.show && jQuery.fn.hide ) {
QUnit.test( "show(); hide()", function( assert ) { QUnit.test( "show()", 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 ) {
assert.expect( 18 ); assert.expect( 18 );
@ -557,11 +538,20 @@ QUnit.test( "show();", function( assert ) {
jQuery( "<div>test</div> text <span>test</span>" ).hide().remove(); jQuery( "<div>test</div> text <span>test</span>" ).hide().remove();
} ); } );
QUnit.test( "show() resolves correct default display for detached nodes", function( assert ) { QUnit.test( "show/hide detached nodes", function( assert ) {
assert.expect( 16 ); assert.expect( 19 );
var div, span, tr; 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 = jQuery( "<div class='hidden'>" );
div.show().appendTo( "#qunit-fixture" ); div.show().appendTo( "#qunit-fixture" );
assert.equal( div.css( "display" ), "none", 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" ); 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 ) { QUnit[ jQuery.find.compile && jQuery.fn.toggle ? "test" : "skip" ]( "toggle()", function( assert ) {

21
test/unit/effects.js vendored
View File

@ -5,7 +5,11 @@ if ( !jQuery.fx ) {
return; 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", { QUnit.module( "effects", {
setup: function() { setup: function() {
@ -131,16 +135,18 @@ QUnit.test( "show()", function( assert ) {
jQuery( "<div>test</div> text <span>test</span>" ).hide().remove(); jQuery( "<div>test</div> text <span>test</span>" ).hide().remove();
} ); } );
QUnit.test( "show(Number) - other displays", function( assert ) { supportjQuery.each( hideOptions, function( type, setup ) {
QUnit.test( "show(Number) - " + type + " hidden", function( assert ) {
assert.expect( 30 ); assert.expect( 30 );
jQuery( jQuery(
"<div id='show-tests'>" + "<div id='show-tests'>" +
"<div><p><a href='#'></a></p><code></code><pre></pre><span></span></div>" + "<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>" + "<table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody>" +
"</table>" +
"<ul><li></li></ul></div>" + "<ul><li></li></ul></div>" +
"<table id='test-table'></table>" "<table id='test-table'></table>"
).appendTo( "#qunit-fixture" ).find( "*" ).css( "display", "none" ); ).appendTo( "#qunit-fixture" ).find( "*" ).each( setup );
var test, var test,
old = jQuery( "#test-table" ).show().css( "display" ) !== "table"; old = jQuery( "#test-table" ).show().css( "display" ) !== "table";
@ -190,14 +196,16 @@ QUnit.test( "show(Number) - other displays", function( assert ) {
} ); } );
jQuery( "#show-tests" ).remove(); jQuery( "#show-tests" ).remove();
} );
} ); } );
// Supports #7397 // Supports #7397
QUnit.test( "Persist correct display value", function( assert ) { supportjQuery.each( hideOptions, function( type, setup ) {
QUnit.test( "Persist correct display value - " + type + " hidden", function( assert ) {
assert.expect( 3 ); assert.expect( 3 );
jQuery( "<div id='show-tests'><span style='position:absolute;'>foo</span></div>" ) jQuery( "<div id='show-tests'><span style='position:absolute;'>foo</span></div>" )
.appendTo( "#qunit-fixture" ).find( "*" ).css( "display", "none" ); .appendTo( "#qunit-fixture" ).find( "*" ).each( setup );
var $span = jQuery( "#show-tests span" ), var $span = jQuery( "#show-tests span" ),
displayNone = $span.css( "display" ), displayNone = $span.css( "display" ),
@ -223,6 +231,7 @@ QUnit.test( "Persist correct display value", function( assert ) {
clock.tick( 300 ); clock.tick( 300 );
assert.expectJqData( this, $span, "olddisplay" ); assert.expectJqData( this, $span, "olddisplay" );
} );
} ); } );
QUnit.test( "animate(Hash, Object, Function)", function( assert ) { QUnit.test( "animate(Hash, Object, Function)", function( assert ) {