问题
I have a jQuery plugin that moves an element on the screen, but has an "animate" toggle to show a slide transition or not. In attempting to use CSS Transitions rather than Javascript transitions for the change, I ran across this, and I'm not sure if it's a bug/quirk, or I'm just doing it wrong:
var $item = $('#myItem');
if (!animate) {
$item.removeClass('csstransitions'); // Class that has "transition:left 0.25s ease-out"
$('#myItem').css('left', '300px'); // Move the element
$('#myItem').addClass('csstransitions'); // Re-apply transitions class
}
When done this way, where the css change happens while the transitions class is not applied to the element, but is applied immediately after, some browsers (Chrome and Safari, in my testing) still apply the CSS transition, when by my logic, it should just snap to the new location.
See this in action in this jsFiddle; In Chrome or Safari, click the "No Delay" button, and see that it does still animate the position of the box, while the "Delay" button (which uses a timeout set for one millisecond later) doesn't animate the CSS change.
As indicated in the jsFiddle, I'm having to use a setTimeout call (setTimeout(function() { $el.addClass('csstransition'); }, 1);
) to get the proper behavior in Chrome and Safari. Is this just because CSS transitions are bleeding edge, or am I doing something wrong, and there's a simpler way to temporarily "turn of" the transitions?
EDIT: Noticed this question is similar, and while the answer on that one is to just separate the two calls, the question remains of "why do we (web developers) need to separate those two calls?" Is this the method we should be using to toggle CSS transitions?
回答1:
I would vote for quirk, or implementation difference.
Before transitions, it really didn't matter which order styles were applied, because in practicality, order didn't matter, just specificity. But with transitions, an element of time delay was added into styles, which is the crux of the issue.
Not knowing how any of the browsers apply styles, I could guess that Safari and Chrome do some optimizations to not have to re-flow the page after every style update. Instead, they probably wait for particular intervals or events to do the updates, such as at the end of code blocks.
Some of the differences are detailed here:
http://taligarsiel.com/Projects/howbrowserswork1.htm
Although, I don't know if this specific issue is covered.
As demonstration, another way you could handle this is to have 2 click handlers:
$('button#nodelay').on('click', function() {
var $el = $('#square');
$el.removeClass('csstransition');
$el.css('left', '100px');
}).on('click', function() {
$el.addClass('csstransition');
});
This basically divides the two updates into separate code blocks, much like the setTimeout
method.
Also, as transitions are still draft standard, I wouldn't depend on any of this behavior to stay consistent, and there are quirks. (I ran into an issue where transitioning left
and top
at the same time didn't work in all browsers).
Edit
To further explain, if the browser renders all CSS as soon as it is added to the DOM you get this flow:
- CSS
left: 300px
added to DOM element - Style rendered: Browser checks to see if there is a transition on the element. If so, animate, if not, apply immediately. In this case, there is not a transition (yet), so no animation occurs.
- CSS
transition: left 2s ease-out
added to DOM element - Style rendered: No rendering change, transition applies to future
left
changes.
However, if the browser optimizes the rendering of CSS, by grouping at the end of code blocks (or something of the sort), you get this:
- CSS
left: 300px
added to DOM element - CSS
transition: left 2s ease-out
added to DOM element - Rendering point reached (end of code block, etc), all styles rendered:
- When
left
is applied, the browser checks to see if there is a transition on the element. If so, animate, if not, apply immediately. In this case, there is a transition, so the animation occurs.
So, the length of the animation, and the time you wait is irrelevant. What is important is that the left: 300px
gets rendered before the transition is applied. Currently, in WebKit, this means applying the transition
style in a separate, later code block than the left
style. This is accomplished by all of the answers suggested setTimeout
(event with 0 delay), separate click handler (if applied second), or a function callback.
Here is another way that works:
$('button#nodelay').on('click', function() {
var $el = $('#square');
$el.removeClass('csstransition');
$el.css('left', '100px').css('left'); // <-- This (or .show() even)
$el.addClass('csstransition');
});
This works, because you are forcing the browser to stop and evaluate the CSS so that you can get the value of left
(although you could put any valid css attribute in the second .css()
call). In this case, it applies all of the CSS and forces the element(s) to re-render.
来源:https://stackoverflow.com/questions/9774329/css-transitions-applied-even-if-activated-after-state-change