Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trigger CSS transition on appended element

As this question observes, immediate CSS transitions on newly-appended elements are somehow ignored - the end state of the transition is rendered immediately.

For example, given this CSS (prefixes omitted here):

.box { 
  opacity: 0;
  transition: all 2s;
  background-color: red;
  height: 100px;
  width: 100px;
}

.box.in { opacity: 1; }

The opacity of this element will be set immediately to 1:

// Does not animate
var $a = $('<div>')
    .addClass('box a')
    .appendTo('#wrapper');
$a.addClass('in');

I have seen several ways of triggering the transition to get the expected behaviour:

// Does animate
var $b = $('<div>')
    .addClass('box b')
    .appendTo('#wrapper');

setTimeout(function() {
    $('.b').addClass('in');
},0);

// Does animate
var $c = $('<div>')
    .addClass('box c')
    .appendTo('#wrapper');

$c[0]. offsetWidth = $c[0].offsetWidth
$c.addClass('in');

// Does animate
var $d = $('<div>')
    .addClass('box d')
    .appendTo('#wrapper');
$d.focus().addClass('in');

The same methods apply to vanilla JS DOM manipulation - this is not jQuery-specific behaviour.

Edit - I am using Chrome 35.

JSFiddle (includes vanilla JS example).

  • Why are immediate CSS animations on appended elements ignored?
  • How and why do these methods work?
  • Are there other ways of doing it
  • Which, if any, is the preferred solution?
like image 520
joews Avatar asked Jun 10 '14 18:06

joews


People also ask

How do you trigger transitions in CSS?

Triggering transitions You can trigger CSS transitions directly with pseudo classes like :hover (activates when the mouse goes over an element), :focus (activates when a user tabs onto an element, or when a user clicks into an input element), or :active (activates when user clicks on the element).

Does transition only work with hover?

But transitions are not just limited to use with :hover . You can animate CSS properties, thus use CSS transitions without hover. This is done via transitions using some other CSS techniques, a number of which I've outlined below. I've also included a demo for each example.

Which delay CSS property is used for transition effect?

The transition-delay property specifies when the transition effect will start. The transition-delay value is defined in seconds (s) or milliseconds (ms).

Can I transition display CSS?

You can't use the display property in CSS animations either.


4 Answers

The cause of not animating the newly added element is batching reflows by browsers.

When element is added, reflow is needed. The same applies to adding the class. However when you do both in single javascript round, browser takes its chance to optimize out the first one. In that case, there is only single (initial and final at the same time) style value, so no transition is going to happen.

The setTimeout trick works, because it delays the class addition to another javascript round, so there are two values present to the rendering engine, that needs to be calculated, as there is point in time, when the first one is presented to the user.

There is another exception of the batching rule. Browser need to calculate the immediate value, if you are trying to access it. One of these values is offsetWidth. When you are accessing it, the reflow is triggered. Another one is done separately during the actual display. Again, we have two different style values, so we can interpolate them in time.

This is really one of very few occasion, when this behaviour is desirable. Most of the time accessing the reflow-causing properties in between DOM modifications can cause serious slowdown.

The preferred solution may vary from person to person, but for me, the access of offsetWidth (or getComputedStyle()) is the best. There are cases, when setTimeout is fired without styles recalculation in between. This is rare case, mostly on loaded sites, but it happens. Then you won't get your animation. By accessing any calculated style, you are forcing the browser to actually calculate it.

like image 124
Frizi Avatar answered Oct 13 '22 18:10

Frizi


Using jQuery try this (An Example Here.):

var $a = $('<div>')
.addClass('box a')
.appendTo('#wrapper');
$a.css('opacity'); // added
$a.addClass('in');

Using Vanilla javaScript try this:

var e = document.createElement('div');
e.className = 'box e';
document.getElementById('wrapper').appendChild(e);
window.getComputedStyle(e).opacity; // added
e.className += ' in';

Brief idea:

The getComputedStyle() flushes all pending style changes and forces the layout engine to compute the element's current state, hence .css() works similar way.

About css()from jQuery site:

The .css() method is a convenient way to get a style property from the first matched element, especially in light of the different ways browsers access most of those properties (the getComputedStyle() method in standards-based browsers versus the currentStyle and runtimeStyle properties in Internet Explorer) and the different terms browsers use for certain properties.

You may use getComputedStyle()/css() instead of setTimeout. Also you may read this article for some details information and examples.

like image 38
The Alpha Avatar answered Oct 13 '22 18:10

The Alpha


Please use the below code, use "focus()"

Jquery

var $a = $('<div>')
    .addClass('box a')
    .appendTo('#wrapper');
$a.focus(); // focus Added
$a.addClass('in');

Javascript

var e = document.createElement('div');
e.className = 'box e';
document.getElementById('wrapper').appendChild(e).focus(); // focus Added
e.className += ' in';
like image 13
Manu Janardhanan Avatar answered Oct 13 '22 18:10

Manu Janardhanan


I prefer requestAnimationFrame + setTimeout (see this post).

const child = document.createElement("div");
child.style.backgroundColor = "blue";
child.style.width = "100px";
child.style.height = "100px";
child.style.transition = "1s";

parent.appendChild(child);

requestAnimationFrame(() =>
  setTimeout(() => {
    child.style.width = "200px";
  })
);

Try it here.

like image 8
pomber Avatar answered Oct 13 '22 17:10

pomber