Kong
Kong

Reputation: 9546

CSS Transition Not Firing

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 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.

Upvotes: 25

Views: 19506

Answers (6)

Torin Finnemann
Torin Finnemann

Reputation: 781

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.

Upvotes: 48

mike510a
mike510a

Reputation: 2168

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

Upvotes: 1

Josh Burgess
Josh Burgess

Reputation: 9567

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;
}

Upvotes: 4

MMachinegun
MMachinegun

Reputation: 3074

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/

Upvotes: 2

theo - dmno
theo - dmno

Reputation: 627

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

Upvotes: 6

MikeM
MikeM

Reputation: 27405

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;
}

Upvotes: 21

Related Questions