¿Cuál es la forma más limpia de deshabilitar los efectos de transición CSS temporalmente?

210

Tengo un elemento DOM con algunos / todos los siguientes efectos aplicados:

#elem {
  -webkit-transition: height 0.4s ease;
  -moz-transition: height 0.4s ease;
  -o-transition: height 0.4s ease;
  transition: height 0.4s ease;
}

Estoy escribiendo un complemento jQuery que cambia el tamaño de este elemento, necesito deshabilitar estos efectos temporalmente para poder cambiar el tamaño sin problemas.

¿Cuál es la forma más elegante de deshabilitar estos efectos temporalmente (y luego volver a habilitarlos), dado que pueden aplicarse de los padres o no aplicarse en absoluto?

Sam Azafrán
fuente
1
Esto parece prometedor: ricostacruz.com/jquery.transit . Tiene licencia MIT, por lo que puede incorporarlo o estudiarlo para ver cómo lo hace.
Robert Harvey
Todas las respuestas agregan una .notransitionclase. Sería más razonable hacerlo al revés, es decir, apunte #elem.yestransitioncon su CSS y elimine esta clase. Eso elimina tener *s desagradables en su CSS.
marcellothearcane

Respuestas:

484

Respuesta corta

Usa este CSS:

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  transition: none !important;
}

Además, este JS (sin jQuery) ...

someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions

O este JS con jQuery ...

$someElement.addClass('notransition'); // Disable transitions
doWhateverCssChangesYouWant($someElement);
$someElement[0].offsetHeight; // Trigger a reflow, flushing the CSS changes
$someElement.removeClass('notransition'); // Re-enable transitions

... o código equivalente usando cualquier otra biblioteca o marco con el que esté trabajando.

Explicación

Este es realmente un problema bastante sutil.

En primer lugar, probablemente desee crear una clase de 'notransición' que pueda aplicar a los elementos para establecer sus *-transitionatributos CSS none. Por ejemplo:

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  transition: none !important;
}

(Menor a un lado - nota la falta de una -ms-transition. Allí Usted no lo necesita La primera versión de Internet Explorer para las transiciones de apoyo. En absoluto era IE 10, que apoyaron ellas sin prefijo.)

Pero eso es solo estilo, y es la parte fácil. Cuando trates de usar esta clase, te encontrarás con una trampa. La trampa es que un código como este no funcionará de la manera ingenuamente esperada:

// Don't do things this way! It doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
someElement.classList.remove('notransition')

Ingenuamente, puede pensar que el cambio de altura no se animará, porque ocurre mientras se aplica la clase 'notransition'. En realidad, sin embargo, será animado, al menos en todos los navegadores modernos que he probado. El problema es que el navegador está almacenando en caché los cambios de estilo que necesita hacer hasta que JavaScript haya terminado de ejecutarse, y luego está haciendo todos los cambios en un solo reflujo. Como resultado, realiza un reflujo donde no hay un cambio neto en si las transiciones están habilitadas o no, pero hay un cambio neto en la altura. En consecuencia, anima el cambio de altura.

Puede pensar que una forma razonable y limpia de evitar esto sería envolver la eliminación de la clase 'notransition' en un tiempo de espera de 1 ms, como este:

// Don't do things this way! It STILL doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
setTimeout(function () {someElement.classList.remove('notransition')}, 1);

pero esto tampoco funciona de manera confiable. No pude hacer el salto de código anterior en los navegadores WebKit, pero en Firefox (tanto en máquinas lentas como rápidas) a veces (aparentemente al azar) obtendrás el mismo comportamiento que usar el enfoque ingenuo. Supongo que la razón de esto es que es posible que la ejecución de JavaScript sea lo suficientemente lenta como para que la función de tiempo de espera esté esperando para cuando el navegador esté inactivo y de lo contrario estaría pensando en hacer un reflujo oportunista, y si ese escenario sucede, Firefox ejecuta la función en cola antes del reflujo.

La única solución que he encontrado para el problema es forzar un reflujo del elemento, eliminando los cambios de CSS realizados antes de eliminar la clase 'notransition'. Hay varias formas de hacer esto; vea aquí algunas. Lo más parecido a una forma 'estándar' de hacerlo es leer la offsetHeightpropiedad del elemento.

Una solución que realmente funciona, entonces, es

someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions

Aquí hay un violín JS que ilustra los tres enfoques posibles que he descrito aquí (tanto el enfoque exitoso como los dos fallidos): http://jsfiddle.net/2uVAA/131/

Mark Amery
fuente
8
Excelente respuesta. Buen trabajo, y apreciado ya que se me ocurrió el mismo problema. ¿Qué sucede si quisiera eliminar solo un tipo de transición?
rafaelmorais
2
Su solución es correcta, pero para aquellos que buscan más información de fondo: stackoverflow.com/a/31862081/1026
Nickolay
2
Menor de menor importancia a un lado, IE10 fue el primer estable versión de IE a buque con transiciones sin prefijo. Las versiones preliminares, excluyendo la Vista previa de lanzamiento, requerían el prefijo -ms- para las transiciones, junto con una serie de otras cosas. Sospecho que esa es una de las razones por las que el prefijo -ms aparece hoy. La otra razón, mucho más probable, por supuesto, es el culto a la carga habitual que todos hemos llegado a conocer y amar sobre los prefijos de proveedores.
BoltClock
1
Es interesante que simplemente verificando la altura de desplazamiento de un elemento desencadena un reflujo. Ojalá lo supiera hace un año cuando me golpeaba la cabeza contra la pared con este mismo problema. ¿Todavía se considera la forma más ideal de manejar esto?
Brett84c
2
El truco de reflujo no parece funcionar al animar elementos SVG que no tienen un atributo offsetHeight; setTimout funciona.
piotr_cz
19

Yo recomendaría deshabilitar la animación como sugiere DaneSoul, pero haciendo que el cambio sea global:

/*kill the transitions on any descendant elements of .notransition*/
.notransition * { 
  -webkit-transition: none !important; 
  -moz-transition: none !important; 
  -o-transition: none !important; 
  -ms-transition: none !important; 
  transition: none !important; 
} 

.notransitionluego se puede aplicar al bodyelemento, anulando efectivamente cualquier animación de transición en la página:

$('body').toggleClass('notransition');
ov
fuente
2
En realidad, esta es la opción perfecta de la OMI. Especialmente * ayuda mucho, asegurándose de que ninguna de las animaciones secundarias se active. Gracias, votado.
SchizoDuckie
Esta es la respuesta correcta para situaciones en las que solo desea hacer todo de una vez. Por ejemplo, quiero deshabilitar las transiciones mientras giro en dispositivos móviles y esto es perfecto para eso.
Ben Lachman
Terminé usando esto en angularjs, en un escenario algo complicado, pero por Dios esto fue muy útil después de días de golpearme la cabeza.
Aditya MP
2
En cuanto al rendimiento, no puedo esperar que sea una buena idea. "¡Oh, solo aplica esto a TODO en la página, eso facilita las cosas!"
Lodewijk
1
Probablemente debería seleccionar ".notransition" y ".notransition *" para que sea completamente efectivo.
Nathan
19

Agregue una clase CSS adicional que bloquee la transición y luego retírela para volver al estado anterior. Esto hace que los códigos CSS y JQuery sean cortos, simples y bien entendibles.

CSS :

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  -ms-transition: none !important;
  transition: none !important;
}

!important se agregó para asegurarse de que esta regla tendrá más "peso", ya que la ID normalmente es más específica que la clase.

JQuery :

$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
DaneSoul
fuente
55
-1 porque esto no fue suficiente para resolver el problema en Chrome o Firefox: si tengo Javascript que agrega la clase, realiza los cambios y elimina la clase, a veces los cambios aún se animan. Pasé la última hora o dos estudiando este problema con cierto detalle y publicaré una respuesta más tarde con violines que muestran casos en los que falla el enfoque ingenuo, más un buen truco que soluciona la solución aquí al forzar un reflujo entre hacer los cambios y eliminar la clase 'notransition'.
Mark Amery
9

Para una solución JS puro (no hay clases CSS), acaba de establecer la transitiona 'none'. Para restaurar la transición como se especifica en el CSS, establezca el transitionen una cadena vacía.

// Remove the transition
elem.style.transition = 'none';

// Restore the transition
elem.style.transition = '';

Si usa prefijos de proveedor, también deberá establecerlos.

elem.style.webkitTransition = 'none'
Thomas Higginbotham
fuente
8

Puede deshabilitar la animación, transición, transformaciones para todos los elementos en la página con este código CSS

var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '* {' +
'/*CSS transitions*/' +
' -o-transition-property: none !important;' +
' -moz-transition-property: none !important;' +
' -ms-transition-property: none !important;' +
' -webkit-transition-property: none !important;' +
'  transition-property: none !important;' +
'/*CSS transforms*/' +
'  -o-transform: none !important;' +
' -moz-transform: none !important;' +
'   -ms-transform: none !important;' +
'  -webkit-transform: none !important;' +
'   transform: none !important;' +
'  /*CSS animations*/' +
'   -webkit-animation: none !important;' +
'   -moz-animation: none !important;' +
'   -o-animation: none !important;' +
'   -ms-animation: none !important;' +
'   animation: none !important;}';
   document.getElementsByTagName('head')[0].appendChild(style);
Ali
fuente
1
En primer lugar, esto es increíble, ¡gracias! En segundo lugar, también hay algunas otras propiedades que deberían establecerse; ver github.com/japgolly/test-state/blob/master/util/shared/src/test/…
Golly
@Golly Esas otras propiedades podrían afectar el diseño final
jonperl
0

Creo que podría crear una clase css separada que puede usar en estos casos:

.disable-transition {
  -webkit-transition: none;
  -moz-transition: none;
  -o-transition: color 0 ease-in;
  -ms-transition: none;
  transition: none;
}

Luego, en jQuery, alternaría la clase así:

$('#<your-element>').addClass('disable-transition');
Código ciclónico
fuente
Sí, creo que este es el mejor enfoque, de hecho, el complemento puede inyectar ese bloque CSS en la página
Sam Saffron
3
¿Estás seguro de que sin? Una regla de clase importante anulará la ID uno?
DaneSoul
0

Si desea una solución simple sin jquery para evitar todas las transiciones:

  1. Agregue este CSS:
body.no-transition * {
  transition: none !important;
}
  1. Y luego en tu js:
document.body.classList.add("no-transition");

// do your work, and then either immediately remove the class:

document.body.classList.remove("no-transition");

// or, if browser rendering takes longer and you need to wait until a paint or two:

setTimeout(() => document.body.classList.remove("no-transition"), 1);

// (try changing 1 to a larger value if the transition is still applying)
mrm
fuente
-1

Si desea eliminar las transiciones, transformaciones y animaciones CSS de la página web actual, simplemente puede ejecutar este pequeño script que escribí (dentro de la consola de su navegador):

let filePath = "https://dl.dropboxusercontent.com/s/ep1nzckmvgjq7jr/remove_transitions_from_page.css";
let html = `<link rel="stylesheet" type="text/css" href="${filePath}">`;
document.querySelector("html > head").insertAdjacentHTML("beforeend", html);

Utiliza vanillaJS para cargar este archivo css . Aquí también hay un repositorio de Github en caso de que desee usar esto en el contexto de un raspador (Ruby-Selenium): remove-CSS-animations-repo

Thomas
fuente
-3

hace

$('#elem').css('-webkit-transition','none !important'); 

en tu js matarlo?

obviamente repito para cada uno.

Chris
fuente
1
luego debe restablecerlo después del hecho, por lo que debe almacenarlo, lo que llevaría a una buena cantidad de placa de caldera
Sam Saffron
-4

Tendría una clase en su CSS como esta:

.no-transition { 
  -webkit-transition: none;
  -moz-transition: none;
  -o-transition: none;
  -ms-transition: none;
  transition: none;
}

y luego en tu jQuery:

$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it
Moin Zaman
fuente
1
¿Estás seguro de que sin? Una regla de clase importante anulará la ID uno?
DaneSoul
1
Sí, lo hará porque ahora está apuntando a # elem.no-transition que es más específico que solo la identificación
Moin Zaman
3
@MoinZaman Erm, pero no has apuntado #elem.no-transitionen tu CSS, solo has apuntado .no-transition. ¿Quizás quisiste escribir #elem.no-transitionen tu CSS?
Mark Amery