Apa cara paling bersih untuk menonaktifkan efek transisi CSS sementara?

210

Saya memiliki elemen DOM dengan beberapa / semua efek berikut diterapkan:

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

Saya menulis plugin jQuery yang mengubah ukuran elemen ini, saya harus menonaktifkan efek ini sementara sehingga saya dapat mengubah ukurannya dengan lancar.

Apa cara paling elegan untuk menonaktifkan efek ini sementara (dan kemudian mengaktifkannya kembali), mengingat mereka dapat diterapkan dari orang tua atau mungkin tidak diterapkan sama sekali.

Sam Saffron
sumber
1
Ini terlihat menjanjikan: ricostacruz.com/jquery.transit . Ini adalah lisensi MIT, sehingga Anda bisa memasukkannya, atau mempelajarinya untuk melihat bagaimana dia melakukannya.
Robert Harvey
Semua jawaban menambahkan sebuah .notransitionkelas. Akan lebih masuk akal untuk melakukannya sebaliknya, yaitu target #elem.yestransitiondengan css Anda dan menghapus kelas ini. Itu menghilangkan memiliki jahat *di css Anda.
marcellothearcane

Jawaban:

484

Jawaban singkat

Gunakan CSS ini:

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

Ditambah JS ini (tanpa 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

Atau JS ini dengan jQuery ...

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

... atau kode yang setara menggunakan pustaka atau kerangka kerja apa pun yang Anda gunakan.

Penjelasan

Ini sebenarnya masalah yang cukup halus.

Pertama, Anda mungkin ingin membuat kelas 'notransition' yang dapat Anda terapkan ke elemen untuk mengatur *-transitionatribut CSS mereka none. Misalnya:

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

(Samping kecil - perhatikan kurangnya -ms-transitiondi sana. Anda tidak memerlukannya. Versi pertama dari Internet Explorer yang mendukung transisi sama sekali adalah IE 10, yang mendukungnya tidak diperbaiki.)

Tapi itu hanya gaya, dan sedikit mudah. Ketika Anda datang untuk mencoba dan menggunakan kelas ini, Anda akan menjebak. Perangkapnya adalah bahwa kode seperti ini tidak akan berfungsi seperti yang Anda harapkan secara naif:

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

Secara naif, Anda mungkin berpikir bahwa perubahan ketinggian tidak akan dianimasikan, karena itu terjadi ketika kelas 'notransition' diterapkan. Pada kenyataannya, ini akan dianimasikan, setidaknya di semua browser modern yang saya coba. Masalahnya adalah bahwa browser sedang melakukan caching perubahan styling yang perlu dibuat sampai JavaScript selesai dieksekusi, dan kemudian membuat semua perubahan dalam satu reflow tunggal. Akibatnya, ia melakukan reflow di mana tidak ada perubahan bersih pada apakah transisi diaktifkan atau tidak, tetapi ada perubahan bersih pada ketinggian. Akibatnya, itu menghidupkan perubahan tinggi.

Anda mungkin berpikir cara yang masuk akal dan bersih untuk menyiasatinya adalah dengan membungkus penghapusan kelas 'notransisi' dalam waktu habis 1 ms, seperti ini:

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

tapi ini tidak bisa diandalkan. Saya tidak dapat membuat kode di atas di browser WebKit, tetapi pada Firefox (pada mesin lambat dan cepat) Anda kadang-kadang (tampaknya secara acak) mendapatkan perilaku yang sama dengan menggunakan pendekatan naif. Saya kira alasan untuk ini adalah bahwa eksekusi JavaScript mungkin cukup lambat sehingga fungsi timeout menunggu untuk dieksekusi pada saat browser idle dan sebaliknya berpikir untuk melakukan reflow oportunistik, dan jika skenario itu terjadi, Firefox menjalankan fungsi antrian sebelum reflow.

Satu-satunya solusi yang saya temukan untuk masalah ini adalah memaksa reflow elemen, membersihkan perubahan CSS yang dibuat untuk itu, sebelum menghapus kelas 'notransition'. Ada berbagai cara untuk melakukan ini - lihat di sini untuk beberapa. Hal yang paling dekat dengan cara 'standar' dalam melakukan ini adalah membaca offsetHeightproperti elemen.

Salah satu solusi yang benar-benar berfungsi adalah

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

Berikut ini biola JS yang menggambarkan tiga pendekatan yang mungkin saya jelaskan di sini (baik pendekatan yang berhasil maupun yang gagal): http://jsfiddle.net/2uVAA/131/

Mark Amery
sumber
8
Excelente menjawab. Kerja bagus, dan dihargai karena saya menemukan masalah yang sama. Bagaimana jika saya ingin menghapus hanya satu jenis transisi?
rafaelmorais
2
Solusi Anda benar, tetapi bagi mereka yang mencari informasi latar belakang lebih lanjut: stackoverflow.com/a/31862081/1026
Nickolay
2
Selain minor minor, IE10 adalah versi stabil pertama IE yang dikirimkan dengan transisi yang tidak diperbaiki. Versi pra-rilis, tidak termasuk Preview Rilis, memerlukan awalan -ms- untuk transisi, bersama dengan banyak hal lainnya. Saya menduga, itulah salah satu alasan awalan -ms- muncul hari ini. Alasan lain, tentu saja, yang jauh lebih mungkin, alasan adalah kultus kargo biasa yang kita semua ketahui dan sukai tentang awalan vendor.
BoltClock
1
Menarik bahwa hanya memeriksa ketinggian offset elemen memicu reflow. Seandainya saya tahu ini setahun yang lalu ketika saya membenturkan kepala ke dinding dengan masalah yang sama. Apakah ini masih dianggap cara paling ideal untuk menangani ini?
Brett84c
2
Trik reflow tampaknya tidak berfungsi ketika menghidupkan elemen SVG yang tidak memiliki atribut offsetHeight; setTimout berfungsi.
piotr_cz
19

Saya akan menganjurkan penonaktifan animasi seperti yang disarankan oleh DaneSoul, tetapi membuat perubahan 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; 
} 

.notransitionkemudian dapat diterapkan ke bodyelemen, secara efektif mengesampingkan setiap animasi transisi pada halaman:

$('body').toggleClass('notransition');
Ov
sumber
2
Sebenarnya ini adalah opsi sempurna imo. Terutama * sangat membantu, memastikan bahwa tidak ada sub animasi yang dipicu. Terima kasih, terunggah.
SchizoDuckie
Ini adalah jawaban yang tepat untuk situasi di mana Anda hanya ingin melakukan semuanya sekaligus. Misalnya, saya ingin menonaktifkan transisi sambil memutar di ponsel dan ini sangat cocok untuk itu.
Ben Lachman
Saya akhirnya menggunakan ini dalam angularjs, dalam skenario yang agak rumit, tetapi demi Tuhan ini sangat membantu setelah berhari-hari membenturkan kepala.
Aditya MP
2
Dari segi kinerja, saya tidak bisa berharap ini menjadi ide yang bagus. "Oh, terapkan saja ini pada SEMUA yang ada di halaman, itu membuat semuanya lebih mudah!"
Lodewijk
1
Sebaiknya pilih ".notransition" dan ".notransition *" agar sepenuhnya efektif.
Nathan
19

Tambahkan kelas CSS tambahan yang memblokir transisi, dan kemudian hapus untuk kembali ke keadaan sebelumnya. Ini membuat kode CSS dan JQuery pendek, sederhana dan mudah dipahami.

CSS :

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

!important ditambahkan untuk memastikan bahwa aturan ini akan memiliki lebih banyak "bobot", karena ID biasanya lebih spesifik daripada kelas.

JQuery :

$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
DaneSoul
sumber
5
-1 karena ini tidak cukup untuk menyelesaikan masalah bagi saya di Chrome atau Firefox - jika saya memiliki Javascript yang menambahkan kelas, membuat perubahan, dan menghapus kelas, terkadang perubahan masih bernyawa. Saya telah menghabiskan satu atau dua jam terakhir mempelajari masalah ini secara terperinci dan akan mengirim jawaban nanti dengan biola yang menunjukkan kasus-kasus di mana pendekatan naif gagal, ditambah peretasan rapi yang memperbaiki solusi di sini dengan memaksa sebuah reflow antara membuat perubahan dan menghapus kelas 'notransition'.
Mark Amery
9

Untuk solusi JS murni (tanpa kelas CSS), cukup atur transitionke 'none'. Untuk mengembalikan transisi seperti yang ditentukan dalam CSS, setel transitionke string kosong.

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

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

Jika Anda menggunakan awalan vendor, Anda harus mengaturnya juga.

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

Anda dapat menonaktifkan animasi, transisi, trasforms untuk semua elemen di halaman dengan kode css ini

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
sumber
1
Pertama ini luar biasa, terima kasih! Kedua ada juga beberapa properti lain yang harus diatur; melihat github.com/japgolly/test-state/blob/master/util/shared/src/test/...
Waduh
@Golly Properti-properti lain itu dapat memengaruhi desain akhir
jonperl
0

Saya pikir Anda bisa membuat kelas css terpisah yang dapat Anda gunakan dalam kasus ini:

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

Kemudian di jQuery Anda akan beralih kelas seperti:

$('#<your-element>').addClass('disable-transition');
Cyclonecode
sumber
ya saya pikir ini adalah pendekatan terbaik, sebenarnya plugin dapat menyuntikkan blok css ke halaman
Sam Saffron
3
Apakah Anda yakin tanpa! Penguasa CLASS yang penting akan menimpa ID satu?
DaneSoul
0

Jika Anda ingin solusi tanpa jquery sederhana untuk mencegah semua transisi:

  1. Tambahkan CSS ini:
body.no-transition * {
  transition: none !important;
}
  1. Dan kemudian di js Anda:
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
sumber
-1

Jika Anda ingin menghapus transisi, transformasi, dan animasi CSS dari halaman web saat ini, Anda bisa menjalankan skrip kecil yang saya tulis ini (di dalam konsol browser Anda):

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

Ini menggunakan vanillaJS untuk memuat file css ini . Ini juga repo github jika Anda ingin menggunakan ini dalam konteks scraper (Ruby-Selenium): remove-CSS-animations-repo

thomas
sumber
-3

tidak

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

di js kamu membunuhnya?

jelas ulangi untuk masing-masing.

Chris
sumber
1
maka Anda perlu mengatur ulang setelah fakta, jadi Anda perlu menyimpannya, yang akan menyebabkan jumlah pelat ketel yang cukup
Sam Saffron
-4

Saya memiliki kelas di CSS Anda seperti ini:

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

dan kemudian di jQuery Anda:

$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it
Moin Zaman
sumber
1
Apakah Anda yakin tanpa! Penguasa CLASS yang penting akan menimpa ID satu?
DaneSoul
1
Ya, itu karena sekarang Anda menargetkan # elem.no-transisi yang lebih spesifik daripada hanya id
Moin Zaman
3
@MoinZaman Erm, tetapi Anda belum menargetkan #elem.no-transitionCSS Anda, Anda baru saja menargetkan .no-transition. Mungkin Anda bermaksud menulis #elem.no-transitiondi CSS Anda?
Mark Amery