Commit 9df8bd20 authored by Richard Gibson's avatar Richard Gibson

CSS: Ignore the CSS cascade in show()/hide()/etc.

Fixes gh-1767
Fixes gh-2071
Closes gh-2180

(cherry picked from commit 86419b10bfa5e3b71a7d416288ab806d47a31d1f)

Conflicts:
	src/css.js
	src/css/defaultDisplay.js
	src/effects.js
	test/data/testsuite.css
	test/unit/css.js
	test/unit/effects.js
parent d18b6457
...@@ -10,15 +10,15 @@ define([ ...@@ -10,15 +10,15 @@ define([
"./css/curCSS", "./css/curCSS",
"./css/var/swap", "./css/var/swap",
"./css/adjustCSS", "./css/adjustCSS",
"./css/defaultDisplay",
"./css/addGetHookIf", "./css/addGetHookIf",
"./css/support", "./css/support",
"./css/showHide",
"./core/init", "./core/init",
"./core/ready", "./core/ready",
"./selector" // contains "./selector" // contains
], function( jQuery, pnum, access, rmargin, rcssNum, rnumnonpx, cssExpand, isHidden, ], function( jQuery, pnum, access, rmargin, rcssNum, rnumnonpx, cssExpand, isHidden,
curCSS, swap, adjustCSS, defaultDisplay, addGetHookIf, support ) { curCSS, swap, adjustCSS, addGetHookIf, support, showHide ) {
var var
// BuildExclude // BuildExclude
...@@ -67,61 +67,6 @@ function vendorPropName( style, name ) { ...@@ -67,61 +67,6 @@ function vendorPropName( style, name ) {
return origName; return origName;
} }
function showHide( elements, show ) {
var display, elem, hidden,
values = [],
index = 0,
length = elements.length;
for ( ; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
values[ index ] = jQuery._data( elem, "olddisplay" );
display = elem.style.display;
if ( show ) {
// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !values[ index ] && display === "none" ) {
elem.style.display = "";
}
// Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
if ( elem.style.display === "" && isHidden( elem ) ) {
values[ index ] = jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
}
} else {
hidden = isHidden( elem );
if ( display && display !== "none" || !hidden ) {
jQuery._data(
elem,
"olddisplay",
hidden ? display : jQuery.css( elem, "display" )
);
}
}
}
// Set the display of most of the elements in a second loop
// to avoid the constant reflow
for ( index = 0; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
elem.style.display = show ? values[ index ] || "" : "none";
}
}
return elements;
}
function setPositiveNumber( elem, value, subtract ) { function setPositiveNumber( elem, value, subtract ) {
var matches = rnumsplit.exec( value ); var matches = rnumsplit.exec( value );
return matches ? return matches ?
......
define([
"../core",
"../manipulation" // appendTo
], function( jQuery ) {
var iframe,
elemdisplay = {
// Support: Firefox
// We have to pre-define these values for FF (#10227)
HTML: "block",
BODY: "block"
};
/**
* Retrieve the actual display of a element
* @param {String} name nodeName of the element
* @param {Object} doc Document object
*/
// Called only from within defaultDisplay
function actualDisplay( name, doc ) {
var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
display = jQuery.css( elem[ 0 ], "display" );
// We don't have any data stored on the element,
// so use "detach" method as fast way to get rid of the element
elem.detach();
return display;
}
/**
* Try to determine the default display value of an element
* @param {String} nodeName
*/
function defaultDisplay( nodeName ) {
var doc = document,
display = elemdisplay[ nodeName ];
if ( !display ) {
display = actualDisplay( nodeName, doc );
// If the simple way fails, read from inside an iframe
if ( display === "none" || !display ) {
// Use the already-created iframe if possible
iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" ))
.appendTo( doc.documentElement );
// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
doc = iframe[ 0 ].contentDocument;
// Support: IE
doc.write();
doc.close();
display = actualDisplay( nodeName, doc );
iframe.detach();
}
// Store the correct default display
elemdisplay[ nodeName ] = display;
}
return display;
}
return defaultDisplay;
});
define([], function() {
function showHide( elements, show ) {
var display, elem,
values = [],
index = 0,
length = elements.length;
// Determine new display value for elements that need to change
for ( ; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
display = elem.style.display;
if ( show ) {
if ( display === "none" ) {
// Restore a pre-hide() value if we have one
values[ index ] = jQuery._data( elem, "display" ) || "";
}
} else {
if ( display !== "none" ) {
values[ index ] = "none";
// Remember the value we're replacing
jQuery._data( elem, "display", display );
}
}
}
// Set the display of the elements in a second loop
// to avoid the constant reflow
for ( index = 0; index < length; index++ ) {
if ( values[ index ] != null ) {
elements[ index ].style.display = values[ index ];
}
}
return elements;
}
return showHide;
});
...@@ -3,16 +3,18 @@ define([ ...@@ -3,16 +3,18 @@ define([
"./var/rcssNum", "./var/rcssNum",
"./css/var/cssExpand", "./css/var/cssExpand",
"./css/var/isHidden", "./css/var/isHidden",
"./css/var/swap",
"./css/adjustCSS", "./css/adjustCSS",
"./css/defaultDisplay", "./css/showHide",
"./core/init", "./core/init",
"./effects/Tween",
"./queue", "./queue",
"./css",
"./deferred", "./deferred",
"./traversing" "./traversing",
], function( jQuery, rcssNum, cssExpand, isHidden, adjustCSS, defaultDisplay ) { "./manipulation",
"./css",
"./effects/Tween"
], function( jQuery, rcssNum, cssExpand, isHidden, swap, adjustCSS, showHide ) {
var var
fxNow, timerId, fxNow, timerId,
...@@ -79,14 +81,15 @@ function createTween( value, prop, animation ) { ...@@ -79,14 +81,15 @@ function createTween( value, prop, animation ) {
function defaultPrefilter( elem, props, opts ) { function defaultPrefilter( elem, props, opts ) {
/* jshint validthis: true */ /* jshint validthis: true */
var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay, var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
isBox = "width" in props || "height" in props,
anim = this, anim = this,
orig = {}, orig = {},
style = elem.style, style = elem.style,
hidden = elem.nodeType && isHidden( elem ), hidden = elem.nodeType && isHidden( elem ),
dataShow = jQuery._data( elem, "fxshow" ); dataShow = jQuery._data( elem, "fxshow" );
// handle queue: false promises // Queue-skipping animations hijack the fx hooks
if ( !opts.queue ) { if ( !opts.queue ) {
hooks = jQuery._queueHooks( elem, "fx" ); hooks = jQuery._queueHooks( elem, "fx" );
if ( hooks.unqueued == null ) { if ( hooks.unqueued == null ) {
...@@ -112,24 +115,69 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -112,24 +115,69 @@ function defaultPrefilter( elem, props, opts ) {
}); });
} }
// height/width overflow pass // Detect show/hide animations
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { for ( prop in props ) {
// Make sure that nothing sneaks out value = props[ prop ];
// Record all 3 overflow attributes because IE does not if ( rfxtypes.test( value ) ) {
// change the overflow attribute when overflowX and delete props[ prop ];
// overflowY are set to the same value toggle = toggle || value === "toggle";
if ( value === ( hidden ? "hide" : "show" ) ) {
// Pretend to be hidden if this is a "show" and
// there is still data from a stopped show/hide
if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
hidden = true;
// Ignore all other no-op show/hide data
} else {
continue;
}
}
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
}
}
// Bail out if this is a no-op like .hide().hide()
propTween = !jQuery.isEmptyObject( props );
if ( !propTween && jQuery.isEmptyObject( orig ) ) {
return;
}
// Restrict "overflow" and "display" styles during box animations
if ( isBox && elem.nodeType === 1 ) {
// Support: IE 9 - 11
// Record all 3 overflow attributes because IE does not infer the shorthand
// from identically-valued overflowX and overflowY
opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
// Set display property to inline-block for height/width // Identify a display type, preferring old show/hide data over the CSS cascade
// animations on inline elements that are having width/height animated restoreDisplay = dataShow && dataShow.display;
if ( restoreDisplay == null ) {
restoreDisplay = jQuery._data( elem, "display" );
}
display = jQuery.css( elem, "display" ); display = jQuery.css( elem, "display" );
if ( display === "none" ) {
display = restoreDisplay || swap( elem, { "display": "" }, function() {
return jQuery.css( elem, "display" );
} );
}
// Test default display if display is currently "none" // Animate inline elements as inline-block
checkDisplay = display === "none" ? if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
jQuery._data( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display; if ( jQuery.css( elem, "float" ) === "none" ) {
if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) { // Restore the original display value at the end of pure show/hide animations
style.display = "inline-block"; if ( !propTween ) {
anim.done(function() {
style.display = restoreDisplay;
});
if ( restoreDisplay == null ) {
display = style.display;
restoreDisplay = display === "none" ? "" : display;
}
}
style.display = "inline-block";
}
} }
} }
...@@ -142,72 +190,52 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -142,72 +190,52 @@ function defaultPrefilter( elem, props, opts ) {
}); });
} }
// show/hide pass // Implement show/hide animations
for ( prop in props ) { propTween = false;
value = props[ prop ]; for ( prop in orig ) {
if ( rfxtypes.exec( value ) ) {
delete props[ prop ];
toggle = toggle || value === "toggle";
if ( value === ( hidden ? "hide" : "show" ) ) {
// If there is dataShow left over from a stopped hide or show // General show/hide setup for this element animation
// and we are going to proceed with show, we should pretend to be hidden if ( !propTween ) {
if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { if ( dataShow ) {
hidden = true; if ( "hidden" in dataShow ) {
} else { hidden = dataShow.hidden;
continue;
} }
} else {
dataShow = jQuery._data( elem, "fxshow", { display: restoreDisplay } );
} }
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
// Any non-fx value stops us from restoring the original display value // Store hidden/visible for toggle so `.stop().toggle()` "reverses"
} else { if ( toggle ) {
display = undefined; dataShow.hidden = !hidden;
} }
}
if ( !jQuery.isEmptyObject( orig ) ) { // Show elements before animating them
if ( dataShow ) { if ( hidden ) {
if ( "hidden" in dataShow ) { showHide( [ elem ], true );
hidden = dataShow.hidden;
} }
} else {
dataShow = jQuery._data( elem, "fxshow", {} );
}
// store state if its toggle - enables .stop().toggle() to "reverse" /* jshint -W083 */
if ( toggle ) {
dataShow.hidden = !hidden;
}
if ( hidden ) {
jQuery( elem ).show();
} else {
anim.done(function() { anim.done(function() {
jQuery( elem ).hide(); // The final step of a "hide" animation is actually hiding the element
if ( !hidden ) {
showHide( [ elem ] );
}
jQuery._removeData( elem, "fxshow" );
for ( prop in orig ) {
jQuery.style( elem, prop, orig[ prop ] );
}
}); });
} }
anim.done(function() {
var prop; // Per-property setup
jQuery._removeData( elem, "fxshow" ); propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
for ( prop in orig ) { if ( !( prop in dataShow ) ) {
jQuery.style( elem, prop, orig[ prop ] ); dataShow[ prop ] = propTween.start;
} if ( hidden ) {
}); propTween.end = propTween.start;
for ( prop in orig ) { propTween.start = prop === "width" || prop === "height" ? 1 : 0;
tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
if ( !( prop in dataShow ) ) {
dataShow[ prop ] = tween.start;
if ( hidden ) {
tween.end = tween.start;
tween.start = prop === "width" || prop === "height" ? 1 : 0;
}
} }
} }
// If this is a noop like .hide().hide(), restore an overwritten display value
} else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
style.display = display;
} }
} }
......
...@@ -76,7 +76,8 @@ div.noopacity { ...@@ -76,7 +76,8 @@ div.noopacity {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(opacity=0)"; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(opacity=0)";
} }
div.hidden { div.hidden,
span.hidden {
display: none; display: none;
} }
...@@ -124,10 +125,6 @@ div#fx-tests div.noback { ...@@ -124,10 +125,6 @@ div#fx-tests div.noback {
display: none; 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; } #nothiddendiv { font-size: 16px; }
#nothiddendivchild.em { font-size: 2em; } #nothiddendivchild.em { font-size: 2em; }
#nothiddendivchild.prct { font-size: 150%; } #nothiddendivchild.prct { font-size: 150%; }
...@@ -135,11 +132,6 @@ div#show-tests * { display: none; } ...@@ -135,11 +132,6 @@ div#show-tests * { display: none; }
/* For testing type on vml in IE #7071 */ /* For testing type on vml in IE #7071 */
v\:oval { behavior:url(#default#VML); display:inline-block; } v\:oval { behavior:url(#default#VML); display:inline-block; }
/* 8099 changes to default styles are read correctly */
tt { display: none; }
sup { display: none; }
dfn { display: none; }
/* #9239 Attach a background to the body( avoid crashes in removing the test element in support ) */ /* #9239 Attach a background to the body( avoid crashes in removing the test element in support ) */
body, div { background: url(http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif) no-repeat -1000px 0; } body, div { background: url(http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif) no-repeat -1000px 0; }
......
This diff is collapsed.
...@@ -57,7 +57,7 @@ function testWidth( val ) { ...@@ -57,7 +57,7 @@ function testWidth( val ) {
equal( jQuery(window).width(), document.documentElement.clientWidth, "Window width is equal to width reported by window/document." ); equal( jQuery(window).width(), document.documentElement.clientWidth, "Window width is equal to width reported by window/document." );
QUnit.expectJqData( this, $div[0], "olddisplay" ); QUnit.expectJqData( this, $div[0], "display" );
} }
test("width()", function() { test("width()", function() {
...@@ -110,7 +110,7 @@ function testHeight( val ) { ...@@ -110,7 +110,7 @@ function testHeight( val ) {
equal( jQuery(window).height(), document.documentElement.clientHeight, "Window width is equal to width reported by window/document." ); equal( jQuery(window).height(), document.documentElement.clientHeight, "Window width is equal to width reported by window/document." );
QUnit.expectJqData( this, $div[0], "olddisplay" ); QUnit.expectJqData( this, $div[0], "display" );
} }
test("height()", function() { test("height()", function() {
...@@ -165,7 +165,7 @@ test("innerWidth()", function() { ...@@ -165,7 +165,7 @@ test("innerWidth()", function() {
equal( div.innerWidth(), 0, "Make sure that disconnected nodes are handled." ); equal( div.innerWidth(), 0, "Make sure that disconnected nodes are handled." );
div.remove(); div.remove();
QUnit.expectJqData( this, $div[ 0 ], "olddisplay" ); QUnit.expectJqData( this, $div[ 0 ], "display" );
}); });
test("innerHeight()", function() { test("innerHeight()", function() {
...@@ -200,7 +200,7 @@ test("innerHeight()", function() { ...@@ -200,7 +200,7 @@ test("innerHeight()", function() {
equal( div.innerHeight(), 0, "Make sure that disconnected nodes are handled." ); equal( div.innerHeight(), 0, "Make sure that disconnected nodes are handled." );
div.remove(); div.remove();
QUnit.expectJqData( this, $div[ 0 ], "olddisplay" ); QUnit.expectJqData( this, $div[ 0 ], "display" );
}); });
test("outerWidth()", function() { test("outerWidth()", function() {
...@@ -239,7 +239,7 @@ test("outerWidth()", function() { ...@@ -239,7 +239,7 @@ test("outerWidth()", function() {
equal( div.outerWidth(), 0, "Make sure that disconnected nodes are handled." ); equal( div.outerWidth(), 0, "Make sure that disconnected nodes are handled." );
div.remove(); div.remove();
QUnit.expectJqData( this, $div[ 0 ], "olddisplay" ); QUnit.expectJqData( this, $div[ 0 ], "display" );
}); });
test("child of a hidden elem (or unconnected node) has accurate inner/outer/Width()/Height() see #9441 #9300", function() { test("child of a hidden elem (or unconnected node) has accurate inner/outer/Width()/Height() see #9441 #9300", function() {
...@@ -385,7 +385,7 @@ test("outerHeight()", function() { ...@@ -385,7 +385,7 @@ test("outerHeight()", function() {
equal( div.outerHeight(), 0, "Make sure that disconnected nodes are handled." ); equal( div.outerHeight(), 0, "Make sure that disconnected nodes are handled." );
div.remove(); div.remove();
QUnit.expectJqData( this, $div[ 0 ], "olddisplay" ); QUnit.expectJqData( this, $div[ 0 ], "display" );
}); });
test("passing undefined is a setter #5571", function() { test("passing undefined is a setter #5571", function() {
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment