Activar la transición CSS en el elemento anexado


Como observa esta pregunta, las transiciones CSS inmediatas en los elementos recién agregados se ignoran de alguna manera: el estado final de la transición se renderiza inmediatamente.

Por ejemplo, dado este CSS (prefijos omitidos aquí):

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

.box.in { opacity: 1; }

La opacidad de este elemento se establecerá inmediatamente en 1:

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

He visto varias formas de activar la transición para obtener el comportamiento esperado:

// 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');

Los mismos métodos se aplican a la manipulación JS DOM de vainilla - este no es un comportamiento específico de jQuery.

Editar - Estoy usando Chrome 35.

JSFiddle (incluye el ejemplo de vanilla JS).

  • ¿Por qué se ignoran las animaciones CSS inmediatas en los elementos anexados?
  • ¿Cómo y por qué funcionan estos métodos?
  • Hay otras formas de hacerlo
  • ¿Cuál, en su caso, es la solución preferida?
Author: Community, 2014-06-10

6 answers

La causa de que no se anime el elemento recién agregado es que los navegadores realicen reflujos por lotes.

Cuando se agrega un elemento, se necesita reflujo. Lo mismo se aplica a la adición de la clase. Sin embargo, cuando haces ambas cosas en single javascript round, el navegador aprovecha la oportunidad para optimizar la primera. En ese caso, solo hay un valor de estilo único (inicial y final al mismo tiempo), por lo que no va a suceder ninguna transición.

El truco setTimeout funciona, porque retrasa la adición de clase a otra ronda de javascript, por lo que hay dos valores presentes en el motor de renderizado, que debe calcularse, ya que hay un punto en el tiempo, cuando el primero se presenta al usuario.

Hay otra excepción de la regla de procesamiento por lotes. El navegador debe calcular el valor inmediato, si está tratando de acceder a él. Uno de estos valores es offsetWidth. Cuando está accediendo a él, se activa el reflujo. Otro se hace por separado durante la visualización real. Una vez más, tenemos dos estilos diferentes valores, para que podamos interpolarlos en el tiempo.

Esta es realmente una de las pocas ocasiones en que este comportamiento es deseable. La mayoría de las veces, acceder a las propiedades que causan reflujo entre las modificaciones del DOM puede causar una desaceleración grave.

La solución preferida puede variar de persona a persona, pero para mí, el acceso de offsetWidth (o getComputedStyle()) es la mejor. Hay casos, cuando setTimeout se dispara sin recalculación de estilos en el medio. Este es un caso raro, sobre todo en sitios cargados, pero suceder. Entonces no tendrás tu animación. Al acceder a cualquier estilo calculado, está forzando al navegador a calcularlo realmente.

 86
Author: Frizi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 12:50:38

Usando jQuerypruebe esto ( Un Ejemplo Aquí.):

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

Usando JavaScript Vanilla prueba esto:

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

Breve idea:

El getComputedStyle () limpia todos los cambios de estilo pendientes y fuerza al motor de diseño a calcular el estado actual del elemento, por lo tanto .css () funciona de manera similar.

Acerca de css() desde jQuery sitio:

El.el método css() es una forma conveniente de obtener una propiedad de estilo primero elemento emparejado, especialmente a la luz de las diferentes formas los navegadores acceden a la mayoría de esas propiedades (el getComputedStyle() método en navegadores basados en estándares frente a currentStyle y Propiedades de runtimeStyle en Internet Explorer) y los diferentes términos los navegadores utilizan para ciertas propiedades.

Puede usar getComputedStyle()/css() en lugar de setTimeout. También puede leer este artículo para obtener información detallada y ejemplos.

 25
Author: The Alpha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-13 09:07:31

Utilice el siguiente código, utilice"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';
 5
Author: Manu Janardhanan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-18 06:56:18

La solución de@Frizi funciona, pero a veces he encontrado que getComputedStyle no ha funcionado cuando cambio ciertas propiedades en un elemento. Si eso no funciona, puedes probar getBoundingClientRect() de la siguiente manera, que he encontrado que es a prueba de balas:

Supongamos que tenemos un elemento el, en el que queremos hacer una transición opacity, pero el es display:none; opacity: 0:

el.style.display = 'block';
el.style.transition = 'opacity .5s linear';

// reflow
el.getBoundingClientRect();

// it transitions!
el.style.opacity = 1;
 4
Author: nikk wong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-01 07:29:33

En lugar de intentar forzar un repintado inmediato o un cálculo de estilo, intenté usar requestAnimationFrame() para permitir que el navegador pintara en su siguiente marco disponible.

En Chrome + Firefox, el navegador optimiza demasiado el renderizado, por lo que esto todavía no ayuda (funciona en Safari).

Decidí forzar manualmente un retraso con setTimeout() y luego usar requestAnimationFrame() para dejar que el navegador pinte de manera responsable. Si el anexo no se ha pintado antes de que finalice el tiempo de espera, la animación puede ser ignorada, pero parece funcionar fiable.

setTimeout(function () {
    requestAnimationFrame(function () {
        // trigger the animation
    });
}, 20);

Elegí 20ms porque es más grande que 1 frame a 60 fps (16.7 ms) y algunos navegadores no registran tiempos de espera

Crucemos los dedos para forzar el inicio de la animación en el siguiente fotograma y luego iniciarlo responsablemente cuando el navegador esté listo para pintar de nuevo.

 2
Author: Brendan Falkowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-15 19:18:37

A diferencia de Brendan , encontré que requestAnimationFrame() trabajó en Chrome 63, Firefox 57, IE11 y Edge.

var div = document.createElement("div");
document.body.appendChild(div);

requestAnimationFrame(function () {
  div.className = "fade";
});
div {
  height: 100px;
  width: 100px;
  background-color: red;
}

.fade {
  opacity: 0;
  transition: opacity 2s;
}
 0
Author: James T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-05 21:14:27