Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is setTimeout needed when applying a class for my transition to take effect?

I have an element with a transition applied to it. I want to control the transition by adding a class to the element which causes the transition to run. However, if I apply the class too quickly, the transition effect does not take place.

I'm assuming this is because the .shown is placed onto the div during the same event loop as when .foo is placed onto the DOM. This tricks the browser into thinking that it was created with opacity: 1 so no transition is put into place.

I'm wondering if there is an elegant solution to this rather than wrapping my class in a setTimeout.

Here's a snippet:

var foo = $('<div>', {
    'class': 'foo'
});

foo.appendTo($('body'));

setTimeout(function(){
    foo.addClass('shown');
});
.foo {
    opacity: 0;
    transition: opacity 5s ease;
    width: 200px;
    height: 200px;
    background-color: red;
}

.foo.shown {
    opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
like image 787
Sean Anderson Avatar asked Sep 17 '14 21:09

Sean Anderson


2 Answers

Actually, the point is not about the setTimeout, but about how the element is rendered.

The CSS transition will only appear if the element is rendered with a property value, and then this property is changed. But once you append the element, it does not mean that it was rendered. Simply adding a setTimeout is not enough. Thought it may work for you, in some browser versions it won't work! (Mostly Firefox)

The point is about the element's render time. Instead of setTimeout, you can force a DOM render by requesting a visual style property, and then changing the class:

var foo = $('<div>', {
    'class': 'foo'
});

foo.appendTo($('body'));

//Here I request a visual render.
var x = foo[0].clientHeight;

//And it works, without setTimeout
foo.addClass('shown');
.foo {
    opacity: 0;
    transition: opacity 5s ease;
    width: 200px;
    height: 200px;
    background-color: red;
}

.foo.shown {
    opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
like image 59
LcSalazar Avatar answered Oct 19 '22 12:10

LcSalazar


When you do DOM manipulation that that javascript relies on immediately afterwards, you need to pause javascript execution briefly in order to allow rendering to catch up, since that will be done asynchronously. All a blank setTimeout does is move the code within to the end of the current execution pipeline. The browser must complete rendering the new layout before it will obey a trigger for your transition so the setTimeout is a good idea and in my opinion the most elegant solution.

like image 42
Brad Wells Avatar answered Oct 19 '22 12:10

Brad Wells