Qual é a maneira mais limpa de desativar temporariamente os efeitos de transição CSS?

210

Eu tenho um elemento DOM com alguns / todos os seguintes efeitos 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;
}

Estou escrevendo um plugin jQuery que está redimensionando esse elemento. Preciso desativar esses efeitos temporariamente para poder redimensioná-lo sem problemas.

Qual é a maneira mais elegante de desativar temporariamente esses efeitos (e depois reativá-los), dado que eles podem ser aplicados pelos pais ou podem nem ser aplicados.

Sam Saffron
fonte
1
Isso parece promissor: ricostacruz.com/jquery.transit . É licenciado pelo MIT, então você pode incorporá-lo ou estudá-lo para ver como ele o faz.
Robert Harvey
Todas as respostas adicionam uma .notransitionclasse. Seria mais razoável fazer o contrário, ou seja, direcionar #elem.yestransitioncom seu css e remover esta classe. Isso remove ter *s desagradáveis em seu css.
marcellothearcane

Respostas:

484

Resposta curta

Use este CSS:

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

Além disso, este JS (sem 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 esse JS com 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 código equivalente usando qualquer outra biblioteca ou estrutura com a qual você esteja trabalhando.

Explicação

Este é realmente um problema bastante sutil.

Primeiro, você provavelmente deseja criar uma classe 'notransition' que possa ser aplicada aos elementos para definir seus *-transitionatributos CSS none. Por exemplo:

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

(Minor lado - nota a falta de um -ms-transition. Lá você não precisa dele A primeira versão do Internet Explorer para transições de apoio. Em tudo foi IE 10, que os apoiaram unprefixed.)

Mas isso é apenas estilo e é fácil. Quando você tentar usar essa classe, encontrará uma armadilha. A armadilha é que um código como este não funcionará da maneira que você poderia esperar ingenuamente:

// 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, você pode pensar que a mudança na altura não será animada, porque acontece enquanto a classe 'notransition' é aplicada. Na realidade, porém, será animado, pelo menos em todos os navegadores modernos que tentei. O problema é que o navegador está armazenando em cache as alterações de estilo que ele precisa fazer até que o JavaScript termine de executar e, em seguida, fazendo todas as alterações em um único reflow. Como resultado, ele faz um reflow em que não há alteração líquida para se as transições estão ou não ativadas, mas há uma alteração líquida na altura. Consequentemente, anima a mudança de altura.

Você pode pensar que uma maneira razoável e limpa de contornar isso seria envolver a remoção da classe 'notransition' em um tempo limite de 1 ms, assim:

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

mas isso também não funciona de maneira confiável. Não consegui quebrar o código acima nos navegadores WebKit, mas no Firefox (em máquinas lentas e rápidas) às vezes você (aparentemente aleatoriamente) obtém o mesmo comportamento que usa a abordagem ingênua. Acho que o motivo disso é que é possível que a execução do JavaScript seja lenta o suficiente para que a função timeout esteja aguardando para ser executada no momento em que o navegador estiver ocioso e, caso contrário, estaria pensando em fazer um reflow oportunista e, se esse cenário acontecer, O Firefox executa a função na fila antes do reflow.

A única solução que encontrei para o problema é forçar um reflow do elemento, liberando as alterações de CSS feitas nele antes de remover a classe 'notransition'. Existem várias maneiras de fazer isso - veja aqui algumas. O mais próximo que existe de uma maneira "padrão" de fazer isso é ler a offsetHeightpropriedade do elemento.

Uma solução que realmente funciona, então, é

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

Aqui está um violino de JS que ilustra as três abordagens possíveis que descrevi aqui (a abordagem bem-sucedida e as duas sem êxito): http://jsfiddle.net/2uVAA/131/

Mark Amery
fonte
8
Excelente resposta. Bom trabalho e apreciado desde que me deparei com o mesmo problema. E se eu quisesse remover apenas um tipo de transição?
Rafaelmorais
2
Sua solução está certa, mas para quem procura mais informações básicas: stackoverflow.com/a/31862081/1026
Nickolay
2
Menor secundário à parte, o IE10 foi a primeira versão estável do IE a ser fornecida com transições não pré-fixadas. As versões de pré-lançamento, excluindo a Visualização da versão, exigiam o prefixo -ms- para transições, além de várias outras coisas. Suspeito que essa seja uma das razões pelas quais o prefixo -ms- aparece hoje. A outra razão, muito mais provável, é claro, é o culto à carga habitual que todos conhecemos e amamos sobre os prefixos de fornecedores.
BoltClock
1
Interessante que simplesmente verificar a altura de deslocamento de um elemento desencadeie um reflow. Gostaria de saber isso há um ano, quando estava batendo a cabeça contra a parede com o mesmo problema. Ainda é considerada a maneira mais ideal de lidar com isso?
Brett84c
2
O truque de refluxo parece não funcionar ao animar elementos SVG que não possuem um atributo offsetHeight; setTimout funciona.
Piotr_cz 16/08/19
19

Eu recomendaria desativar a animação, conforme sugerido por DaneSoul, mas tornando a mudança 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; 
} 

.notransitionpode ser aplicado ao bodyelemento, substituindo efetivamente qualquer animação de transição na página:

$('body').toggleClass('notransition');
ov
fonte
2
Na verdade, esta é a opção perfeita. Especialmente o * ajuda muito, certificando-se de que nenhuma das sub-animações seja acionada. Obrigado, votado.
SchizoDuckie
Esta é a resposta certa para situações em que você só quer fazer tudo de uma vez. Por exemplo, eu quero desativar as transições enquanto gira no celular, e isso é perfeito para isso.
Ben Lachman
Acabei usando isso em angularjs, em um cenário um pouco complicado, mas, por Deus, isso foi muito útil depois de dias de batidas na cabeça.
Aditya MP
2
Em termos de desempenho, não posso esperar que seja uma boa ideia. "Ah, basta aplicar isso a TUDO na página, para facilitar as coisas!"
Lodewijk
1
Provavelmente deve selecionar ".notransition" e ".notransition *" para ser totalmente eficaz.
Nathan
19

Adicione uma classe CSS adicional que bloqueie a transição e remova-a para retornar ao estado anterior. Isso torna o código CSS e JQuery curto, simples e bem compreensível.

CSS :

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

!important foi adicionado para garantir que essa regra tenha mais "peso", porque o ID normalmente é mais específico que a classe.

JQuery :

$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
DaneSoul
fonte
5
-1 porque isso não foi suficiente para resolver o problema para mim no Chrome ou Firefox - se eu tiver Javascript que adicione a classe, faça as alterações e remova a classe, às vezes as alterações ainda serão animadas. Passei a última hora ou duas estudando esse problema detalhadamente e publicarei uma resposta posteriormente com violinos, mostrando casos em que a abordagem ingênua falha, além de um hack puro que corrige a solução aqui, forçando um refluxo entre fazer as alterações e remover a classe 'notransição'.
Mark Amery
9

Para uma solução JS pura (sem classes CSS), basta definir transitioncomo 'none'. Para restaurar a transição conforme especificado no CSS, defina transitioncomo uma sequência vazia.

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

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

Se você estiver usando prefixos de fornecedores, precisará defini-los também.

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

Você pode desativar a animação, a transição e as transformações para todos os elementos da página com 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);
Todos
fonte
1
Em primeiro lugar, isso é incrível, obrigado! Em segundo lugar, há também algumas outras propriedades que devem ser definidas; veja github.com/japgolly/test-state/blob/master/util/shared/src/test/…
Golly
@Golly Essas outras propriedades podem afetar o design final
jonperl
0

Eu acho que você poderia criar uma classe css separada que você pode usar nesses casos:

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

Em jQuery, você alternaria a classe da seguinte maneira:

$('#<your-element>').addClass('disable-transition');
Cyclonecode
fonte
sim, eu acho que essa é a melhor abordagem, na verdade, o plugin pode injetar esse bloco css na página
Sam Saffron
3
Tem certeza de que, sem! A importante regra CLASS substituirá a ID um?
precisa saber é o seguinte
0

Se você deseja uma solução simples sem jquery para impedir todas as transições:

  1. Adicione este CSS:
body.no-transition * {
  transition: none !important;
}
  1. E então em seus 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)
srm
fonte
-1

Se você deseja remover transições, transformações e animações de CSS da página atual, basta executar este pequeno script que escrevi (no console do seu 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);

Ele usa o vanillaJS para carregar esse arquivo css . Aqui também está um repositório do github, caso você queira usá-lo no contexto de um raspador (Ruby-Selenium): remove-CSS-animations-repo

Thomas
fonte
-3

faz

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

em seus js matá-lo?

obviamente repita para cada um.

Chris
fonte
1
então você precisa para redefini-la após o fato, então você precisa armazená-lo, o que levaria a uma boa quantidade de placa de caldeira
Sam Saffron
-4

Eu teria uma classe no seu CSS assim:

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

e depois no seu jQuery:

$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it
Moin Zaman
fonte
1
Tem certeza de que, sem! A importante regra CLASS substituirá a ID um?
precisa saber é o seguinte
1
Sim, ele vai porque agora você está alvejando # elem.no-transição, que é mais específico do que apenas o id
Moin Zaman
3
@ MooZaman Erm, mas você não segmentou #elem.no-transitionem seu CSS, apenas segmentou .no-transition. Talvez você quisesse escrever #elem.no-transitionno seu CSS?
Mark Amery