Quelle est la façon la plus propre de désactiver temporairement les effets de transition CSS?

210

J'ai un élément DOM avec certains / tous les effets suivants appliqués:

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

J'écris un plugin jQuery qui redimensionne cet élément, je dois désactiver ces effets temporairement afin de pouvoir le redimensionner en douceur.

Quelle est la façon la plus élégante de désactiver temporairement ces effets (puis de les réactiver), étant donné qu'ils peuvent être appliqués par les parents ou pas du tout.

Sam Saffron
la source
1
Cela semble prometteur: ricostacruz.com/jquery.transit . Il est sous licence MIT, vous pouvez donc l'incorporer ou l'étudier pour voir comment il le fait.
Robert Harvey
Toutes les réponses ajoutent une .notransitionclasse. Il serait plus raisonnable de le faire dans l'autre sens, c'est-à-dire de cibler #elem.yestransitionavec votre CSS et de supprimer cette classe. Cela supprime les méchants *s dans votre CSS.
marcellothearcane

Réponses:

484

Réponse courte

Utilisez ce CSS:

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

Plus soit ce JS (sans 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

Ou ce JS avec jQuery ...

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

... ou du code équivalent utilisant n'importe quelle autre bibliothèque ou framework avec lequel vous travaillez.

Explication

Il s'agit en fait d'un problème assez subtil.

Tout d'abord, vous souhaiterez probablement créer une classe «notransition» que vous pouvez appliquer aux éléments pour définir leurs *-transitionattributs CSS none. Par exemple:

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

(Mineure de côté - notez l'absence d'un -ms-transition. Vous n'en avez pas besoin. La première version d'Internet Explorer pour prendre en charge les transitions du tout était IE 10, qui les supportait préfixés.)

Mais ce n'est que du style et c'est la partie la plus facile. Lorsque vous venez d'essayer et d'utiliser cette classe, vous rencontrerez un piège. Le piège est que le code comme celui-ci ne fonctionnera pas comme vous pourriez vous y attendre naïvement:

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

Naïvement, vous pourriez penser que le changement de hauteur ne sera pas animé, car il se produit lorsque la classe «notransition» est appliquée. En réalité, cependant, il sera animé, du moins dans tous les navigateurs modernes que j'ai essayés. Le problème est que le navigateur met en cache les modifications de style qu'il doit apporter jusqu'à ce que JavaScript soit terminé, puis effectue toutes les modifications en une seule redistribution. Par conséquent, il effectue une refusion où il n'y a pas de changement net quant à l'activation ou non des transitions, mais il y a un changement net de la hauteur. Par conséquent, il anime le changement de hauteur.

Vous pourriez penser qu'un moyen raisonnable et propre de contourner ce problème serait d'encapsuler la suppression de la classe «notransition» dans un délai de 1 ms, comme ceci:

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

mais cela ne fonctionne pas de manière fiable non plus. Je n'ai pas pu faire la rupture de code ci-dessus dans les navigateurs WebKit, mais sur Firefox (sur les machines lentes et rapides), vous obtiendrez parfois (apparemment au hasard) le même comportement qu'en utilisant l'approche naïve. Je suppose que la raison en est qu'il est possible que l'exécution de JavaScript soit suffisamment lente pour que la fonction de délai d'attente soit en attente d'exécution au moment où le navigateur est inactif et penserait autrement à faire une redistribution opportuniste, et si ce scénario se produit, Firefox exécute la fonction en file d'attente avant la redistribution.

La seule solution que j'ai trouvée au problème est de forcer une redistribution de l'élément, en vidant les modifications CSS qui y sont apportées, avant de supprimer la classe 'notransition'. Il existe différentes façons de le faire - voir ici pour certains. La chose la plus proche d'une méthode «standard» est de lire leoffsetHeight propriété de l'élément.

Une solution qui fonctionne alors est

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

Voici un violon JS qui illustre les trois approches possibles que j'ai décrites ici (à la fois l'approche réussie et les deux infructueuses): http://jsfiddle.net/2uVAA/131/

Mark Amery
la source
8
Réponse Excelente. Bon travail et apprécié depuis que j'ai rencontré le même problème. Et si je voulais supprimer un seul type de transition?
rafaelmorais
2
Votre solution est bonne, mais pour ceux qui recherchent plus d'informations de fond: stackoverflow.com/a/31862081/1026
Nickolay
2
Mineur mineur de côté, IE10 a été la première version stable d'IE à être livrée avec des transitions non préfixées. Les versions préliminaires, à l'exception de l'aperçu de la version, nécessitaient le préfixe -ms- pour les transitions, ainsi qu'une multitude d'autres choses. C'est, je suppose, l'une des raisons pour lesquelles le préfixe -ms- apparaît aujourd'hui. L'autre raison, beaucoup plus probable bien sûr, est le culte du fret habituel que nous connaissons et aimons tous à propos des préfixes de fournisseurs.
BoltClock
1
Il est intéressant de noter que la simple vérification de la hauteur de décalage d'un élément déclenche une refusion. J'aurais aimé le savoir il y a un an quand je me cognais la tête contre le mur avec ce même problème. Est-ce toujours considéré comme le moyen le plus idéal de gérer cela?
Brett84c
2
L'astuce de refusion ne semble pas fonctionner lors de l'animation d'éléments SVG qui n'ont pas d'attribut offsetHeight; setTimout fonctionne.
piotr_cz
19

Je recommanderais de désactiver l'animation comme suggéré par DaneSoul, mais de rendre le changement 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; 
} 

.notransitionpeut ensuite être appliqué à l' bodyélément, remplaçant efficacement toute animation de transition sur la page:

$('body').toggleClass('notransition');
ov
la source
2
En fait, c'est l'option parfaite imo. Surtout le * aide beaucoup, en s'assurant qu'aucune des sous-animations n'est déclenchée. Merci, a voté.
SchizoDuckie
C'est la bonne réponse pour les situations où vous voulez tout faire en même temps. Par exemple, je souhaite désactiver les transitions lors de la rotation sur mobile et c'est parfait pour cela.
Ben Lachman
J'ai fini par l'utiliser dans angularjs, dans un scénario quelque peu compliqué, mais par dieu, c'était si utile après des jours à me cogner la tête.
Aditya MP
2
En termes de performances, je ne peux pas du tout espérer que ce soit une bonne idée. "Oh, applique simplement ceci à TOUT sur la page, cela rend les choses plus faciles!"
Lodewijk
1
Devrait probablement sélectionner à la fois ".notransition" et ".notransition *" pour être pleinement efficace.
Nathan
19

Ajoutez une classe CSS supplémentaire qui bloque la transition, puis supprimez-la pour revenir à l'état précédent. Cela rend le code CSS et JQuery court, simple et bien compréhensible.

CSS :

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

!important a été ajouté pour être sûr que cette règle aura plus de "poids", car l'ID est normalement plus spécifique que la classe.

JQuery :

$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
DaneSoul
la source
5
-1 parce que ce n'était pas suffisant pour résoudre le problème pour moi dans Chrome ou Firefox - si j'ai Javascript qui ajoute la classe, fait les changements et supprime la classe, parfois les changements sont toujours animés. J'ai passé la dernière heure ou deux à étudier ce problème en détail et publierai une réponse plus tard avec des violons montrant des cas où l'approche naïve échoue, ainsi qu'un hack soigné qui corrige la solution ici en forçant une redistribution entre apporter les modifications et supprimer la classe «notransition».
Mark Amery
9

Pour une solution JS pure (pas de classes CSS), définissez simplement le transitionsur 'none'. Pour restaurer la transition comme spécifié dans le CSS, définissez le transitionsur une chaîne vide.

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

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

Si vous utilisez des préfixes de fournisseurs, vous devrez également les définir.

elem.style.webkitTransition = 'none'
Thomas Higginbotham
la source
8

Vous pouvez désactiver l'animation, la transition, les transformations pour tous les éléments de la page avec ce code 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
la source
1
Tout d'abord, c'est génial, merci! Deuxièmement, d'autres propriétés devraient également être définies; voir github.com/japgolly/test-state/blob/master/util/shared/src/test/…
Golly
@Golly Ces autres propriétés pourraient affecter la conception finale
jonperl
0

Je pense que vous pouvez créer une classe CSS distincte que vous pouvez utiliser dans ces cas:

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

Ensuite, dans jQuery, vous basculeriez la classe comme suit:

$('#<your-element>').addClass('disable-transition');
Cyclonecode
la source
oui je pense que c'est la meilleure approche, en fait le plugin peut injecter ce bloc css dans la page
Sam Saffron
3
Êtes-vous sûr que sans! La règle CLASS importante remplacera l'ID un?
DaneSoul
0

Si vous voulez une solution simple sans jquery pour empêcher toutes les transitions:

  1. Ajoutez ce CSS:
body.no-transition * {
  transition: none !important;
}
  1. Et puis dans votre 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
la source
-1

Si vous souhaitez supprimer les transitions, transformations et animations CSS de la page Web actuelle, vous pouvez simplement exécuter ce petit script que j'ai écrit (dans la console de votre navigateur):

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

Il utilise vanillaJS pour charger ce fichier css . Voici également un dépôt github au cas où vous voudriez l'utiliser dans le contexte d'un grattoir (Ruby-Selenium): remove-CSS-animations-repo

Thomas
la source
-3

Est-ce que

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

dans votre js le tuer?

répéter évidemment pour chacun.

Chris
la source
1
alors vous devez le réinitialiser après coup, vous devez donc le stocker, ce qui conduirait à une bonne quantité de plaque de chaudière
Sam Saffron
-4

J'aurais une classe dans votre CSS comme ceci:

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

puis dans votre jQuery:

$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it
Moin Zaman
la source
1
Êtes-vous sûr que sans! La règle CLASS importante remplacera l'ID un?
DaneSoul
1
Oui, ce sera le cas car vous ciblez maintenant # elem.no-transition, qui est plus spécifique que l'ID
Moin Zaman
3
@MoinZaman Erm, mais vous n'avez pas ciblé #elem.no-transitiondans votre CSS, vous venez de cibler .no-transition. Peut-être vouliez-vous écrire #elem.no-transitiondans votre CSS?
Mark Amery