Qual è il modo più pulito per disabilitare temporaneamente gli effetti di transizione CSS?


215

Ho un elemento DOM con alcuni / tutti i seguenti effetti applicati:

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

Sto scrivendo un plugin jQuery che sta ridimensionando questo elemento, devo disabilitare temporaneamente questi effetti in modo da poterlo ridimensionare senza problemi.

Qual è il modo più elegante per disabilitare temporaneamente questi effetti (e poi riattivarli), dato che possono essere applicati dai genitori o potrebbero non essere applicati affatto.


1
Questo sembra promettente: ricostacruz.com/jquery.transit . È autorizzato dal MIT, quindi puoi incorporarlo o studiarlo per vedere come lo fa.
Robert Harvey

Tutte le risposte aggiungono una .notransitionclasse. Sarebbe più ragionevole farlo al contrario, cioè targetizzare #elem.yestransitioncon il tuo css e rimuovere questa classe. Ciò elimina la presenza di messaggi cattivi *nel tuo css.
marcellothearcane

Risposte:


494

Risposta breve

Usa questo CSS:

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

Inoltre o questo JS (senza 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 questo 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 codice equivalente utilizzando qualsiasi altra libreria o framework con cui stai lavorando.

Spiegazione

Questo è in realtà un problema abbastanza sottile.

Per prima cosa, probabilmente vorrai creare una classe "notransition" che puoi applicare agli elementi per impostare i loro *-transitionattributi CSS none. Per esempio:

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

(Minore a parte - di notare che la mancanza di un -ms-transitionin là Non è necessario che la prima versione di Internet Explorer per le transizioni di supporto.. A tutti era IE 10, che ha sostenuto li senza prefisso).

Ma questo è solo stile ed è la parte facile. Quando verrai per provare a usare questa lezione, ti imbatterai in una trappola. La trappola è che un codice come questo non funzionerà nel modo in cui potresti ingenuamente aspettarti:

// 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, potresti pensare che il cambio di altezza non sarà animato, perché avviene mentre viene applicata la classe 'notransition'. In realtà, però, esso farà animato, almeno in tutti i browser moderni che ho provato. Il problema è che il browser memorizza nella cache le modifiche di stile che deve apportare fino al termine dell'esecuzione di JavaScript, quindi apporta tutte le modifiche in un unico reflow. Di conseguenza, esegue un reflow in cui non vi è alcuna modifica netta per stabilire se le transizioni sono abilitate o meno, ma c'è una modifica netta dell'altezza. Di conseguenza, anima il cambio di altezza.

Potresti pensare che un modo ragionevole e pulito per aggirare questo problema sarebbe racchiudere la rimozione della classe 'notransition' in un timeout di 1 ms, come questo:

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

ma neanche questo funziona in modo affidabile. Non sono stato in grado di eseguire la suddetta interruzione di codice nei browser WebKit, ma su Firefox (su macchine sia lente che veloci) a volte (apparentemente a caso) otterrai lo stesso comportamento dell'approccio ingenuo. Immagino che il motivo sia che è possibile che l'esecuzione di JavaScript sia abbastanza lenta da far sì che la funzione di timeout sia in attesa di essere eseguita nel momento in cui il browser è inattivo e altrimenti starebbe pensando di eseguire un reflow opportunistico e, se lo scenario si verifica Firefox esegue la funzione in coda prima del reflow.

L'unica soluzione che ho trovato al problema è forzare un reflow dell'elemento, svuotando le modifiche CSS apportate ad esso, prima di rimuovere la classe "notransition". Ci sono vari modi per farlo - vedi qui per alcuni. La cosa più vicina a un modo "standard" di farlo è leggere la offsetHeightproprietà dell'elemento.

Una soluzione che funziona davvero, quindi, è

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

Ecco un violino JS che illustra i tre possibili approcci che ho descritto qui (sia quello riuscito che i due non riusciti): http://jsfiddle.net/2uVAA/131/


8
Risposta eccellente. Buon lavoro e apprezzato da quando ho avuto lo stesso problema. E se volessi rimuovere solo un tipo di transizione?
rafaelmorais

2
La tua soluzione è giusta, ma per chi cerca ulteriori informazioni di base: stackoverflow.com/a/31862081/1026
Nickolay

2
Minore minore a parte, IE10 è stata la prima versione stabile di IE fornita con transizioni non fissate. Le versioni pre-rilascio, esclusa l'anteprima di rilascio, richiedevano il prefisso -ms- per le transizioni, insieme a un gran numero di altre cose. Questo, sospetto, è uno dei motivi per cui il prefisso -ms- appare oggi. L'altro motivo, molto più probabile, è il solito culto del carico che tutti abbiamo imparato a conoscere e ad amare riguardo ai prefissi dei venditori.
BoltClock

1
È interessante che il semplice controllo dell'altezza di offset di un elemento inneschi un reflow. Vorrei saperlo un anno fa quando sbattevo la testa contro il muro con lo stesso problema. Questo è ancora considerato il modo più ideale per gestirlo?
Brett84c

2
Il trucco del reflow non sembra funzionare quando si animano elementi SVG che non hanno un attributo offsetHeight; setTimout funziona.
piotr_cz

20

Suggerirei di disabilitare l'animazione come suggerito da DaneSoul, ma rendendo lo switch globale:

/*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; 
} 

.notransitionpuò quindi essere applicato bodyall'elemento, sostituendo efficacemente qualsiasi animazione di transizione sulla pagina:

$('body').toggleClass('notransition');

2
In realtà questa è l'opzione perfetta imo. Soprattutto * aiuta molto, assicurandosi che nessuna delle animazioni secondarie venga attivata. Grazie, voto positivo.
SchizoDuckie

Questa è la risposta giusta per le situazioni in cui vuoi fare tutto in una volta. Ad esempio, voglio disabilitare le transizioni durante la rotazione sul cellulare e questo è perfetto per questo.
Ben Lachman

Ho finito per usarlo in angularjs, in uno scenario un po 'complicato, ma per dio è stato così utile dopo giorni in cui ho sbattuto la testa.
Aditya MP

2
Per quanto riguarda le prestazioni, non posso aspettarmi che questa sia una buona idea. "Oh, applica questo a TUTTO sulla pagina, questo rende le cose più facili!"
Lodewijk

2
Probabilmente dovrebbe selezionare sia ".notransition" che ".notransition *" per essere pienamente efficaci.
Nathan

20

Aggiungi un'ulteriore classe CSS che blocca la transizione, quindi rimuovila per tornare allo stato precedente. Questo rende sia il codice CSS che JQuery brevi, semplici e ben comprensibili.

CSS :

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

!important è stato aggiunto per essere sicuri che questa regola avrà più "peso", perché l'ID è normalmente più specifico della classe.

JQuery :

$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition

5
-1 perché questo non era sufficiente per risolvere il problema per me in Chrome o Firefox - se ho Javascript che aggiunge la classe, apporta le modifiche e rimuove la classe, a volte le modifiche si animano ancora . Ho trascorso le ultime ore o due a studiare questo problema in dettaglio e posterò una risposta in seguito con violini che mostrano casi in cui l'approccio ingenuo fallisce, oltre a un trucco accurato che risolve la soluzione qui forzando un reflow tra apportare le modifiche e rimuovere la classe "notransition".
Mark Amery

10

Per una soluzione JS pura (senza classi CSS), è sufficiente impostare il valore transitionsu 'none'. Per ripristinare la transizione come specificato nel CSS, impostare transitionsu una stringa vuota.

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

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

Se stai utilizzando i prefissi del fornitore, dovrai impostare anche quelli.

elem.style.webkitTransition = 'none'

10

Puoi disabilitare l'animazione, la transizione, le trasformazioni per tutti gli elementi nella pagina con questo codice 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);

1
Innanzitutto è fantastico, grazie! In secondo luogo ci sono anche alcune altre proprietà che dovrebbero essere impostate; vedi github.com/japgolly/test-state/blob/master/util/shared/src/test/…
Golly

@Golly Queste altre proprietà potrebbero influenzare il progetto finale
jonperl

1

Penso che potresti creare una classe CSS separata che puoi usare in questi casi:

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

Quindi in jQuery dovresti attivare la classe in questo modo:

$('#<your-element>').addClass('disable-transition');

sì, penso che questo sia l'approccio migliore, infatti il ​​plugin può iniettare quel blocco CSS nella pagina
Sam Saffron

3
Sei sicuro che senza! Importante righello CLASS avrà la precedenza su ID?
DaneSoul

1

Questa è la soluzione alternativa che ha funzionato facilmente per me. Non è una risposta diretta alla domanda, ma può comunque aiutare qualcuno.

Piuttosto che creare una notransitionclasse che avrebbe dovuto annullare la transizione

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

Ho creato la moveTransitionclasse

.moveTransition {
      -webkit-transition: left 3s, top 3s;
      -moz-transition: left 3s, top 3s;
      -o-transition: left 3s, top 3s;
      transition: left 3s, top 3s;
}

Quindi ho aggiunto questa classe a element con js

element.classList.add("moveTransition")

E più tardi in setTimeout, l'ho rimosso

element.classList.remove("moveTransition")

Non sono stato in grado di testarlo in browser diversi ma in Chrome funziona perfettamente


0

Se vuoi rimuovere transizioni CSS, trasformazioni e animazioni dalla pagina web corrente puoi semplicemente eseguire questo piccolo script che ho scritto (nella console del tuo browser):

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

Usa vanillaJS per caricare questo file css . Ecco anche un repository GitHub nel caso in cui desideri utilizzarlo nel contesto di uno scraper (Ruby-Selenium): remove-CSS-animations-repo


0

Se vuoi una semplice soluzione no-jquery per prevenire tutte le transizioni:

  1. Aggiungi questo CSS:
body.no-transition * {
  transition: none !important;
}
  1. E poi nel tuo 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)

-2

fa

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

nel tuo js ucciderlo?

ovviamente ripetere per ciascuno.


1
quindi è necessario ripristinarlo dopo il fatto, quindi è necessario conservarlo, il che porterebbe a una discreta quantità di piastra della caldaia
Sam Saffron

-3

Avrei una classe nel tuo CSS come questa:

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

e poi nel tuo jQuery:

$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it

1
Sei sicuro che senza! Importante righello CLASS avrà la precedenza su ID?
DaneSoul

1
Sì, lo sarà perché ora stai prendendo di mira # elem.no-Transition, che è più specifico del solo ID
Moin Zaman

4
@MoinZaman Erm, ma non hai mirato #elem.no-transitionnel tuo CSS, hai solo mirato .no-transition. Forse intendevi scrivere #elem.no-transitionnel tuo CSS?
Mark Amery
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.