I'm creating a DOM element (a div), adding it to the DOM, then changing its width all in one quick hit in javascript. This in theory should trigger a CSS3 transition, but the result is straight 开发者_Python百科from A to B, without the transition in between.
If I make the width change through a separate test click event everything works as expected.
Here's my JS and CSS:
JS (jQuery):
var div = $('<div />').addClass('trans').css('width', '20px');
$('#container').append(div);
div.css('width', '200px');
CSS (just mozilla for the minute):
.trans {
-moz-transition-property: all;
-moz-transition-duration: 5s;
height: 20px;
background-color: cyan;
}
Am I messing up here, or is the "all in one quick hit" not the way things should be done?
All help is really appreciated.
A cleaner approach that does not rely on setTimeout, is to read the css property in question before setting it:
var div = $('<div />').addClass('trans');
$('#container').append(div);
div.css('width');//add this line
div.css('width', '200px');
Working here:
var div = $('<div class="trans" />');
$('#container').append(div);
var div = $('<div />').addClass('trans');
$('#container').append(div);
div.css('width');//add this line
div.css('width', '200px');
.trans {
width: 20px;
height: 20px;
background-color: cyan;
-webkit-transition: all 5s ease;
-moz-transition: all 5s ease;
-ie-transition: all 5s ease;
-o-transition: all 5s ease;
transition: all 5s ease;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container"></div>
As explained in the comments below by Lucero, doing it this way is necessary to force the browser to calculate an actual value rather than "auto", "inherit" or similar. Without an actual value, the browser will not know the new value to be a change from the previous.
here are two ways to do this.
1 - CSS transitions
by using setTimeout
the addClass
method will run after instead of along with the preceding script so that the transition
event will fire
example jsfiddle
jQuery:
var div = $('<div class="trans" />');
$('#container').append(div);
// set the class change to run 1ms after adding the div
setTimeout(function() {div.addClass('wide')}, 1);
CSS:
.trans {
width: 20px;
height: 20px;
background-color: cyan;
-webkit-transition: all 5s ease;
-moz-transition: all 5s ease;
-ie-transition: all 5s ease;
-o-transition: all 5s ease;
transition: all 5s ease;
}
.wide {
width: 200px;
}
2 - jQuery's .animate()
function
example jsfiddle
jQuery:
var div = $('<div class="trans" />');
$('#container').append(div);
div.animate({'width': '200px'}, 5000); // 5 sec animation
CSS:
.trans {
width: 20px;
height: 20px;
background-color: cyan;
}
Try calling jQuery's .offset()
method.
When the browser gets the offset, it triggers a reflow/repaint/layout event which allows the transitions to work.
See this fiddle: http://jsfiddle.net/FYPpt/199/
Also see -- http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html
In response to the bounty:
If you don't have the luxury of knowing which property is being transitioned or simply don't think it's good practice to force a setTimeout
to get an animation to fire, you can force a reflow in another way.
The reason that setTimeout
works in the first place is due to the fact that you're causing a full document reflow in the meantime, even if the duration is set to 0
. That's not the only way to cause a reflow.
Link to a fiddle
JavaScript
for (x = Math.ceil(Math.random() * 10), i=0;i<x;i++){
$('<div />').appendTo($('#container')).addClass('trans');
}
var divs = $('#container div');
var x = divs.eq(Math.ceil(Math.random() * divs.length));
x[0].offsetHeight;
x.addClass('wide');
With this JavaScript we're randomly adding between 1 and 10 div
elements, and then selecting one of those at random and adding the wide
class.
You'll notice an odd line of JavaScript in there, namely x[0].offsetHeight
. This is the lynchpin to the whole operation. Calling offsetHeight
triggers the document to reflow without using a setTimeout or querying the CSS value.
What a reflow is in relation to the DOM:
A reflow computes the layout of the page. A reflow on an element recomputes the dimensions and position of the element, and it also triggers further reflows on that element’s children, ancestors and elements that appear after it in the DOM. Then it calls a final repaint. Reflowing is very expensive, but unfortunately it can be triggered easily.
So please be judicious in how you use these functions. Not too much of a concern with most modern browsers (as jQuery is notorious for firing several reflows), but still something to consider before adding something like this solution all over your website.
Important note: This is no more or less efficient than a setTimeout
call. This isn't a magic bullet solution, and it's doing the same thing under the hood.
Here's the corresponding CSS, for reference:
.trans {
width: 20px;
height: 20px;
background-color: cyan;
-webkit-transition: all 5s ease;
-moz-transition: all 5s ease;
-o-transition: all 5s ease;
transition: all 5s ease;
}
.trans.wide {
width: 200px;
}
as @onetrickpony was asking, what if all rules are in the CSS file and should not be added in the JS-function.
Based on the answer from Torin Finnemann, just with a slight change:
Add an extra class where rules are defined: new-rules
var div = $('<div class="trans" />');
$('#container').append(div);
var div = $('<div />').addClass('trans');
$('#container').append(div);
div.height();
div.addClass('new-rules');
in your CSS have the class predefined:
.trans.new-rules {
width: 200px;
background: yellow;
}
now there is no need tingling with the JS when changing the CSS. Change it in the CSS-file at new-rules
:)
http://jsfiddle.net/FYPpt/201/
var div = $('<div />');
$('#container').append(div);
div.css('width', '20px').addClass('trans', function() {
div.css('width', '200px')
});
Creates a new DIV element and adds the 'trans' class to that element in order to fire the CSS transition
精彩评论