Apakah mungkin untuk mendengarkan acara "perubahan gaya"?


206

Apakah mungkin membuat pendengar acara di jQuery yang dapat terikat pada perubahan gaya apa pun? Misalnya, jika saya ingin "melakukan" sesuatu ketika suatu elemen mengubah dimensi, atau perubahan lain dalam atribut style yang dapat saya lakukan:

$('div').bind('style', function() {
    console.log($(this).css('height'));
});

$('div').height(100); // yields '100'

Ini akan sangat berguna.

Ada ide?

MEMPERBARUI

Maaf telah menjawab ini sendiri, tetapi saya menulis solusi yang rapi yang mungkin cocok untuk orang lain:

(function() {
    var ev = new $.Event('style'),
        orig = $.fn.css;
    $.fn.css = function() {
        $(this).trigger(ev);
        return orig.apply(this, arguments);
    }
})();

Ini sementara akan menimpa metode prototipe.css internal dan mendefinisikan kembali dengan pemicu di akhir. Jadi kerjanya seperti ini:

$('p').bind('style', function(e) {
    console.log( $(this).attr('style') );
});

$('p').width(100);
$('p').css('color','red');

ya, solusi yang bagus, tetapi dalam aplikasi nyata, saya curiga ini mungkin membutuhkan banyak sumber daya, dan bahkan membuat browser tampak lambat untuk mengikuti interaksi pengguna. Saya ingin tahu apakah itu masalahnya atau tidak.
Pixeline

1
@ixix: Saya tidak bisa melihat bahwa itu seharusnya jauh lebih lambat. jQuery masih memanggil prototipe css, saya hanya menambahkan pemicu untuk itu. Jadi pada dasarnya hanya satu panggilan metode tambahan.
David Hellsing

Saya mengerti bahwa: sepertinya dalam aliran pengguna berinteraksi dengan suatu aplikasi, css () disebut banyak waktu. Bergantung pada apa yang Anda lakukan dengan pemicu itu (jika itu hanya log konsol, saya kira Anda tentu saja aman), ini dapat menimbulkan masalah. Saya hanya ingin tahu. Itu tergantung pada aplikasinya juga. Saya pribadi cenderung melakukan banyak umpan balik visual dalam barang-barang saya.
Pixeline

Ada beberapa hal lagi yang harus dilakukan untuk membuatnya berfungsi dengan panggilan css jquery yang ada: 1) Anda ingin mengembalikan elemen keluar dari fungsi css baru Anda atau kalau tidak itu akan memutuskan panggilan css gaya lancar. misalnya. $ ('p'). css ('display', 'block'). css ('color', 'black'); dll. 2) jika ini adalah panggilan untuk nilai properti (mis. 'obj.css (' height ');') Anda ingin mengembalikan nilai pengembalian fungsi asli - Saya melakukan ini dengan memeriksa apakah daftar argumen untuk fungsi hanya berisi satu argumen.
theringostarrs

1
Saya membuat versi yang lebih kuat dari pendekatan ini yang juga berfungsi ketika atribut style itu sendiri dihapus. Ini akan mengadakan acara 'gaya' untuk setiap gaya yang dihapus dengan cara ini. gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema

Jawaban:


19

Karena jQuery adalah open-source, saya kira Anda dapat mengubah cssfungsi untuk memanggil fungsi pilihan Anda setiap kali dipanggil (melewati objek jQuery). Tentu saja, Anda ingin menjelajahi kode jQuery untuk memastikan tidak ada lagi yang digunakan secara internal untuk mengatur properti CSS. Idealnya, Anda ingin menulis plugin terpisah untuk jQuery sehingga tidak mengganggu perpustakaan jQuery itu sendiri, tetapi Anda harus memutuskan apakah layak atau tidak untuk proyek Anda.


1
Tetapi dalam hal ini apa yang akan terjadi jika saya mengatur properti style secara lurus dengan fungsi DOM ??
Jaime Hablutzel

Berikut adalah contoh lengkap dari solusi ini: gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema

272

Hal-hal telah berubah sedikit sejak pertanyaan diajukan - sekarang mungkin untuk menggunakan MutationObserver untuk mendeteksi perubahan dalam atribut 'style' elemen, tidak diperlukan jQuery:

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutationRecord) {
        console.log('style changed!');
    });    
});

var target = document.getElementById('myId');
observer.observe(target, { attributes : true, attributeFilter : ['style'] });

Argumen yang diteruskan ke fungsi panggilan balik adalah objek MutationRecord yang memungkinkan Anda mendapatkan nilai gaya lama dan baru.

Dukungan bagus di peramban modern termasuk IE 11+.


19
Perlu diingat bahwa ini hanya bekerja dengan gaya inline, bukan ketika gaya berubah sebagai konsekuensi dari perubahan kelas atau @mediaperubahan.
fregante

2
@ bfred.it apakah Anda punya ide tentang bagaimana mencapai hal itu? Saya ingin menjalankan beberapa js setelah aturan css kelas diterapkan pada elemen.
Kragalon

Anda dapat menggunakannya getComputedStyledengan setIntervaltetapi itu akan banyak memperlambat situs web Anda.
gratis

Catatan: MutationObserver tidak benar-benar berfungsi di semua versi Android 4.4, terlepas dari apa yang dikatakan caniuse.
James Gould

Lihat tipuan ini untuk contoh mengubah warna latar belakang textarea menjadi biru ketika lebar textarea membentang melewati 300px. jsfiddle.net/RyanNerd/y2q8wuf0 (sayangnya tampaknya ada bug di FF yang menggantung skrip diHTMLElement.style.prop = "value"
RyanNerd

18

Deklarasi objek acara Anda harus berada di dalam fungsi css baru Anda. Kalau tidak, acara hanya bisa dipecat sekali.

(function() {
    orig = $.fn.css;
    $.fn.css = function() {
        var ev = new $.Event('style');
        orig.apply(this, arguments);
        $(this).trigger(ev);
    }
})();

Terima kasih banyak. Beberapa orang mengatakan untuk mengubah sumber asli tetapi ekstensi Anda adalah yang terbaik. Saya punya beberapa masalah tetapi akhirnya berhasil.
ccsakuweb

Itu terus menembak, setelah mengeksekusi kode ini, dan bahkan tidak mencapai hasil yang diminta.
smkrn110

13

Saya pikir jawaban terbaik jika dari Mike jika Anda tidak dapat meluncurkan acara Anda karena bukan dari kode Anda. Tapi saya mendapatkan beberapa kesalahan saat menggunakannya. Jadi saya menulis jawaban baru untuk menunjukkan kepada Anda kode yang saya gunakan.

Perpanjangan

// Extends functionality of ".css()"
// This could be renamed if you'd like (i.e. "$.fn.cssWithListener = func ...")
(function() {
    orig = $.fn.css;
    $.fn.css = function() {
        var result = orig.apply(this, arguments);
        $(this).trigger('stylechanged');
        return result;
    }
})();

Pemakaian

// Add listener
$('element').on('stylechanged', function () {
    console.log('css changed');
});

// Perform change
$('element').css('background', 'red');

Saya mendapat kesalahan karena var ev = new $ .Event ('style'); Sesuatu seperti style tidak didefinisikan dalam HtmlDiv .. Saya menghapusnya, dan saya memulai sekarang $ (ini) .trigger ("stylechanged"). Masalah lain adalah bahwa Mike tidak mengembalikan resulto $ (css, ..) maka dapat membuat masalah dalam beberapa kasus. Jadi saya mendapatkan hasilnya dan mengembalikannya. Sekarang berfungsi ^^ Dalam setiap perubahan css, sertakan dari beberapa lib yang tidak dapat saya modifikasi dan picu suatu peristiwa.


Sangat membantu dan pintar
mainkan

5

Seperti yang disarankan orang lain, jika Anda memiliki kendali atas kode apa pun yang mengubah gaya elemen, Anda dapat memecat acara khusus ketika Anda mengubah tinggi elemen:

$('#blah').bind('height-changed',function(){...});
...
$('#blah').css({height:'100px'});
$('#blah').trigger('height-changed');

Jika tidak, meskipun cukup menghabiskan banyak sumber daya, Anda dapat mengatur timer untuk memeriksa perubahan ketinggian elemen secara berkala ...


Ini memang solusi yang sangat rapi jika seseorang memiliki akses ke kode yang mengubah gaya.
Umair Hafeez

2

Tidak ada dukungan bawaan untuk acara perubahan gaya di jQuery atau dalam skrip java. Tetapi jQuery mendukung untuk membuat acara khusus dan mendengarkannya, tetapi setiap kali ada perubahan, Anda harus memiliki cara untuk memicunya sendiri. Jadi itu tidak akan menjadi solusi yang lengkap.


2

Anda dapat mencoba plugin Jquery, itu memicu peristiwa ketika css berubah dan mudah digunakan

http://meetselva.github.io/#gist-section-attrchangeExtension
 $([selector]).attrchange({
  trackValues: true, 
  callback: function (e) {
    //console.log( '<p>Attribute <b>' + e.attributeName +'</b> changed from <b>' + e.oldValue +'</b> to <b>' + e.newValue +'</b></p>');
    //event.attributeName - Attribute Name
    //event.oldValue - Prev Value
    //event.newValue - New Value
  }
});

1

Pertanyaan menarik. Masalahnya adalah ketinggian () tidak menerima panggilan balik, jadi Anda tidak akan dapat mengaktifkan panggilan balik. Gunakan baik animate () atau css () untuk mengatur ketinggian dan kemudian memicu acara khusus di callback. Berikut adalah contoh menggunakan animate (), diuji dan bekerja ( demo ), sebagai bukti konsep:

$('#test').bind('style', function() {
    alert($(this).css('height'));
});

$('#test').animate({height: 100},function(){
$(this).trigger('style');
}); 

8
Maaf, tapi apa ini membuktikan? Anda trigger()acara secara manual. Saya akan berpikir OP adalah setelah beberapa peristiwa yang dipicu otomatis ketika atribut style diubah.
JPot

@JPot dia menunjukkan bahwa atribut ketinggian tidak membuang peristiwa ketika diubah.
AnthonyJClink

1

Hanya menambah dan memformalkan solusi @David dari atas:

Perhatikan bahwa fungsi jQuery dapat diputus-putus dan mengembalikan 'ini' sehingga beberapa pemanggilan dapat dipanggil satu demi satu (mis. $container.css("overflow", "hidden").css("outline", 0); ).

Jadi kode yang ditingkatkan harus:

(function() {
    var ev = new $.Event('style'),
        orig = $.fn.css;
    $.fn.css = function() {
        var ret = orig.apply(this, arguments);
        $(this).trigger(ev);
        return ret; // must include this
    }
})();

0

Bagaimana dengan jQuery cssHooks?

Mungkin saya tidak mengerti pertanyaannya, tetapi apa yang Anda cari mudah dilakukan cssHooks, tanpa mengubahcss() fungsi.

salin dari dokumentasi:

(function( $ ) {

// First, check to see if cssHooks are supported
if ( !$.cssHooks ) {
  // If not, output an error message
  throw( new Error( "jQuery 1.4.3 or above is required for this plugin to work" ) );
}

// Wrap in a document ready call, because jQuery writes
// cssHooks at this time and will blow away your functions
// if they exist.
$(function () {
  $.cssHooks[ "someCSSProp" ] = {
    get: function( elem, computed, extra ) {
      // Handle getting the CSS property
    },
    set: function( elem, value ) {
      // Handle setting the CSS value
    }
  };
});

})( jQuery ); 

https://api.jquery.com/jQuery.cssHooks/


-1

Saya memiliki masalah yang sama, jadi saya menulis ini. Ini bekerja dengan baik. Tampak hebat jika Anda mencampurnya dengan beberapa transisi CSS.

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.