Bagaimana cara mengaktifkan acara pada perubahan kelas menggunakan jQuery?

90

Saya ingin mendapatkan sesuatu seperti:

$('#myDiv').bind('class "submission ok" added'){
    alert('class "submission ok" has been added');
});
Matoeil
sumber

Jawaban:

169

Tidak ada acara yang dimunculkan saat kelas berubah. Alternatifnya adalah dengan memunculkan acara secara manual saat Anda mengubah kelas secara terprogram:

$someElement.on('event', function() {
    $('#myDiv').addClass('submission-ok').trigger('classChange');
});

// in another js file, far, far away
$('#myDiv').on('classChange', function() {
     // do stuff
});

MEMPERBARUI

Pertanyaan ini sepertinya mengumpulkan beberapa pengunjung, jadi berikut adalah pembaruan dengan pendekatan yang dapat digunakan tanpa harus mengubah kode yang sudah ada menggunakan yang baru MutationObserver:

var $div = $("#foo");
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if (mutation.attributeName === "class") {
      var attributeValue = $(mutation.target).prop(mutation.attributeName);
      console.log("Class attribute changed to:", attributeValue);
    }
  });
});
observer.observe($div[0], {
  attributes: true
});

$div.addClass('red');
.red { color: #C00; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo" class="bar">#foo.bar</div>

Ketahuilah bahwa MutationObserverini hanya tersedia untuk browser yang lebih baru, khususnya Chrome 26, FF 14, IE 11, Opera 15 dan Safari 6. Lihat MDN untuk lebih jelasnya. Jika Anda perlu mendukung browser lama, maka Anda perlu menggunakan metode yang saya uraikan di contoh pertama saya.

Rory McCrossan
sumber
Dalam menyelesaikan jawaban ini, saya menemukan ini: stackoverflow.com/a/1950052/1579667
Benj
1
@Benj Itu sama dengan jawaban asli saya (yaitu menggunakan trigger()) meskipun perhatikan bahwa itu sangat ketinggalan jaman karena penggunaan bind(). Saya tidak yakin bagaimana itu 'menyelesaikan' apa pun
Rory McCrossan
Dalam kasus saya, saya perlu mengaktifkan acara ketika node dom memiliki kelas tertentu MutationObserverberfungsi untuk saya
Mario
Ini adalah bentuk perbaikan dari solusi ini dengan filter stackoverflow.com/a/42121795/12074818
MVDeveloper1
20

Anda dapat mengganti fungsi addClass dan removeClass jQuery asli dengan fungsi Anda sendiri yang akan memanggil fungsi asli dan kemudian memicu peristiwa khusus. (Menggunakan fungsi anonim yang memanggil sendiri untuk memuat referensi fungsi asli)

(function( func ) {
    $.fn.addClass = function() { // replace the existing function on $.fn
        func.apply( this, arguments ); // invoke the original function
        this.trigger('classChanged'); // trigger the custom event
        return this; // retain jQuery chainability
    }
})($.fn.addClass); // pass the original function as an argument

(function( func ) {
    $.fn.removeClass = function() {
        func.apply( this, arguments );
        this.trigger('classChanged');
        return this;
    }
})($.fn.removeClass);

Maka sisa kode Anda akan menjadi sesederhana yang Anda harapkan.

$(selector).on('classChanged', function(){ /*...*/ });

Memperbarui:

Pendekatan ini membuat asumsi bahwa kelas hanya akan diubah melalui metode addClass dan removeClass jQuery. Jika kelas dimodifikasi dengan cara lain (seperti manipulasi langsung atribut kelas melalui elemen DOM) penggunaan sesuatu seperti MutationObservers seperti yang dijelaskan dalam jawaban yang diterima di sini akan diperlukan.

Juga sebagai perbaikan pasangan untuk metode ini:

  • Memicu peristiwa untuk setiap kelas yang ditambahkan ( classAdded) atau dihapus ( classRemoved) dengan kelas tertentu yang diteruskan sebagai argumen ke fungsi panggilan balik dan hanya dipicu jika kelas tertentu benar-benar ditambahkan (tidak ada sebelumnya) atau dihapus (sudah ada sebelumnya)
  • Hanya terpicu classChangedjika ada kelas yang benar-benar diubah

    (function( func ) {
        $.fn.addClass = function(n) { // replace the existing function on $.fn
            this.each(function(i) { // for each element in the collection
                var $this = $(this); // 'this' is DOM element in this context
                var prevClasses = this.getAttribute('class'); // note its original classes
                var classNames = $.isFunction(n) ? n(i, prevClasses) : n.toString(); // retain function-type argument support
                $.each(classNames.split(/\s+/), function(index, className) { // allow for multiple classes being added
                    if( !$this.hasClass(className) ) { // only when the class is not already present
                        func.call( $this, className ); // invoke the original function to add the class
                        $this.trigger('classAdded', className); // trigger a classAdded event
                    }
                });
                prevClasses != this.getAttribute('class') && $this.trigger('classChanged'); // trigger the classChanged event
            });
            return this; // retain jQuery chainability
        }
    })($.fn.addClass); // pass the original function as an argument
    
    (function( func ) {
        $.fn.removeClass = function(n) {
            this.each(function(i) {
                var $this = $(this);
                var prevClasses = this.getAttribute('class');
                var classNames = $.isFunction(n) ? n(i, prevClasses) : n.toString();
                $.each(classNames.split(/\s+/), function(index, className) {
                    if( $this.hasClass(className) ) {
                        func.call( $this, className );
                        $this.trigger('classRemoved', className);
                    }
                });
                prevClasses != this.getAttribute('class') && $this.trigger('classChanged');
            });
            return this;
        }
    })($.fn.removeClass);
    

Dengan fungsi pengganti ini, Anda kemudian dapat menangani setiap kelas yang diubah melalui classChanged atau kelas tertentu yang ditambahkan atau dihapus dengan memeriksa argumen ke fungsi callback:

$(document).on('classAdded', '#myElement', function(event, className) {
    if(className == "something") { /* do something */ }
});
Nickolas Hook
sumber
1
Ini bekerja baik, tetapi dalam "sisa kode" event handler harus didelegasikan kepada pemilih target untuk memungkinkan mengikat unsur-unsur baru: $(parent_selector).on('classChanged', target_selector, function(){ /*...*/ });. Butuh beberapa saat bagi saya untuk melihat mengapa elemen yang saya buat secara dinamis tidak mengaktifkan penangan. Lihat learn.jquery.com/events/event-delegation
Timm
2

Anda dapat menggunakan sesuatu seperti ini:

$(this).addClass('someClass');

$(Selector).trigger('ClassChanged')

$(otherSelector).bind('ClassChanged', data, function(){//stuff });

tetapi sebaliknya, tidak, tidak ada fungsi yang telah ditentukan untuk mengaktifkan peristiwa saat kelas berubah.

Baca lebih lanjut tentang pemicu di sini

Deep Sharma
sumber
1
Penangan acara harus item yang sama yang memicu acara tersebut. $ (this) .addClass ('someClass'); $ (Selector) .trigger ('ClassChanged') // Harus sama Selector $ (Selector) .bind ('ClassChanged', data, function () {// stuff});
Meko Perez Estevez
3
Sepertinya Anda telah menyalin dari sini, jika ya maka bagus jika Anda memberikan tautan juga stackoverflow.com/questions/1950038/…
Satinder singh