JQuery: Bagaimana cara memanggil event RESIZE hanya setelah SELESAI mengubah ukurannya?

109

Bagaimana cara memanggil fungsi setelah ukuran jendela browser SELESAI?

Saya mencoba melakukannya seperti itu, tetapi saya mengalami masalah. Saya menggunakan fungsi acara JQuery Resize:

$(window).resize(function() {
  ... // how to call only once the browser has FINISHED resizing?
});

Namun, fungsi ini dipanggil terus menerus jika pengguna mengubah ukuran jendela browser secara manual. Artinya, ia mungkin memanggil fungsi ini puluhan kali dalam selang waktu singkat.

Bagaimana cara memanggil fungsi pengubahan ukuran hanya satu kali (setelah jendela browser selesai mengubah ukuran)?

MEMPERBARUI

Juga tanpa harus menggunakan variabel global.

nickb
sumber
@BGerrissen, jika Anda dapat menunjukkan kepada saya bagaimana melakukan jsfiddle.net/Zevan/c9UE5/1 tanpa variabel global, saya pasti akan :)
nickb
metode cleartimeout / settimeout di atas bekerja dengan sangat baik.
kris-o3

Jawaban:

129

Berikut adalah contoh penggunaan instruksi thejh

Anda dapat menyimpan id referensi ke setInterval atau setTimeout mana pun. Seperti ini:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);
Zevan
sumber
Saya suka bahwa kode ini sangat sederhana. Adakah cara untuk membuat ini berfungsi tanpa harus menggunakan variabel global?
nickb
Jika saya melihat bagaimana melakukan ini tanpa variabel global, saya akan membuat "jawaban diterima"
nickb
tidak masalah. Ada beberapa cara. Saya telah mengedit jawabannya untuk mencerminkan yang paling sederhana yang dapat saya pikirkan.
Zevan
terima kasih atas pembaruannya tetapi tampaknya masih menggunakan variabel global. Bisakah Anda memperbaruinya agar tidak memerlukan variabel global (var id). Terima kasih
nickb
4
Saya pikir Anda melewatkan tautan di akhir posting ... lihat: jsfiddle.net/Zevan/c9UE5/5
Zevan
85

Debounce .

function debouncer( func , timeout ) {
   var timeoutID , timeout = timeout || 200;
   return function () {
      var scope = this , args = arguments;
      clearTimeout( timeoutID );
      timeoutID = setTimeout( function () {
          func.apply( scope , Array.prototype.slice.call( args ) );
      } , timeout );
   }
}


$( window ).resize( debouncer( function ( e ) {
    // do stuff 
} ) );

Catatan, Anda dapat menggunakan metode ini untuk apa pun yang ingin Anda hapus (peristiwa penting, dll).

Sesuaikan parameter batas waktu untuk mendapatkan efek optimal yang diinginkan.

BGerrissen
sumber
1
Saya baru saja mencoba menjalankan ini dan tampaknya menjalankan DUA KALI . Saya mengganti "// lakukan hal" dengan "$ (" body "). Append (" <br/> SELESAI! ");" dan menyebutnya DUA KALI pada perubahan ukuran browser
nickb
Ups ... Saya lupa bagian yang agak penting (sebenarnya menyetel ID waktu tunggu) ... Diperbaiki sekarang, harap coba lagi;)
BGerrissen
3
@ user43493: Ini memanggil cuntion func, dengan thispenunjuk internal menunjuk ke scope, dan dengan Array.prototype.slice.call(args)(yang menghasilkan array standar dari args) sebagai argumen
Eric
4
Ini adalah abstraksi yang dapat digunakan kembali, jadi Anda tidak perlu membuat kode sendiri timeout atau melacak sendiri timeoutID global. Anda dapat menggunakannya untuk lebih dari sekadar mengubah ukuran jendela;) misalnya untuk menolak tombol kirim dengan meneruskan parameter waktu tunggu yang lebih tinggi. Anda dapat memilih solusi kode yang lebih sedikit, tetapi saya menyarankan Anda menyimpan cuplikan ini di kit Anda, Anda akan menghargainya nanti;)
BGerrissen
2
Underscore.js memiliki implementasi yang bagus untuk ini jika Anda sudah menggunakan lib itu. underscorejs.org/#debounce
twmulloy
22

Anda dapat menggunakan setTimeout()dan clearTimeout()berhubungan dengan jQuery.data:

$(window).resize(function() {
    clearTimeout($.data(this, 'resizeTimer'));
    $.data(this, 'resizeTimer', setTimeout(function() {
        //do something
        alert("Haven't resized in 200ms!");
    }, 200));
});

Memperbarui

Saya menulis ekstensi untuk meningkatkan default on(& bind) -event-handler jQuery . Ini melampirkan fungsi pengendali kejadian untuk satu atau lebih kejadian ke elemen yang dipilih jika kejadian itu tidak dipicu untuk interval tertentu. Ini berguna jika Anda ingin mengaktifkan callback hanya setelah penundaan, seperti peristiwa pengubahan ukuran, atau lainnya. https://github.com/yckart/jquery.unevent.js

;(function ($) {
    var methods = { on: $.fn.on, bind: $.fn.bind };
    $.each(methods, function(k){
        $.fn[k] = function () {
            var args = [].slice.call(arguments),
                delay = args.pop(),
                fn = args.pop(),
                timer;

            args.push(function () {
                var self = this,
                    arg = arguments;
                clearTimeout(timer);
                timer = setTimeout(function(){
                    fn.apply(self, [].slice.call(arg));
                }, delay);
            });

            return methods[k].apply(this, isNaN(delay) ? arguments : args);
        };
    });
}(jQuery));

Gunakan seperti yang lainnya onatau bind-event handler, kecuali Anda bisa meneruskan parameter ekstra sebagai yang terakhir:

$(window).on('resize', function(e) {
    console.log(e.type + '-event was 200ms not triggered');
}, 200);

http://jsfiddle.net/ARTsinn/EqqHx/

yckart
sumber
1
Secara harfiah lakukan pencarian google yang sama setiap hari. dan buka halaman yang sama untuk potongan kode ini. Seandainya aku bisa mengingatnya haha.
Dustin Silk
7
var lightbox_resize = false;
$(window).resize(function() {
    console.log(true);
    if (lightbox_resize)
        clearTimeout(lightbox_resize);
    lightbox_resize = setTimeout(function() {
        console.log('resize');
    }, 500);
});
Jlis
sumber
Saya paling suka ini tetapi mengapa tidak menggunakan tanda kurung kurawal pada pernyataan bersyarat Anda? Saya tahu ini berfungsi tanpa mereka, tetapi sulit bagi
developer
7

Hanya untuk menambah hal di atas, adalah umum untuk mendapatkan acara pengubahan ukuran yang tidak diinginkan karena bilah gulir muncul dan keluar, berikut adalah beberapa kode untuk menghindarinya:

function registerResize(f) {
    $(window).resize(function() {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = setTimeout(function() {
            var oldOverflow = document.body.style.overflow;
            document.body.style.overflow = "hidden";
            var currHeight = $(window).height(),
                currWidth = $(window).width();
            document.body.style.overflow = oldOverflow;

            var prevUndefined = (typeof this.prevHeight === 'undefined' || typeof this.prevWidth === 'undefined');
            if (prevUndefined || this.prevHeight !== currHeight || this.prevWidth !== currWidth) {
                //console.log('Window size ' + (prevUndefined ? '' : this.prevHeight + "," + this.prevWidth) + " -> " + currHeight + "," + currWidth);
                this.prevHeight = currHeight;
                this.prevWidth = currWidth;

                f(currHeight, currWidth);
            }
        }, 200);
    });
    $(window).resize(); // initialize
}

registerResize(function(height, width) {
    // this will be called only once per resize regardless of scrollbars changes
});

lihat jsfiddle

kofifus.dll
sumber
5

Underscore.js memiliki beberapa metode bagus untuk tugas ini: throttledan debounce. Meskipun Anda tidak menggunakan Garis Bawah, lihat sumber fungsi ini. Berikut contohnya:

var redraw = function() {'redraw logic here'};
var debouncedRedraw = _.debounce(redraw, 750);
$(window).on('resize', debouncedRedraw);
tybro0103
sumber
2

Ini adalah pendekatan saya:

document.addEventListener('DOMContentLoaded', function(){
    var tos = {};
    var idi = 0;
    var fn  = function(id)
    {
        var len = Object.keys(tos).length;

        if(len == 0)
            return;

        to = tos[id];
        delete tos[id];

        if(len-1 == 0)
            console.log('Resize finished trigger');
    };

    window.addEventListener('resize', function(){
        idi++;
        var id = 'id-'+idi;
        tos[id] = window.setTimeout(function(){fn(id)}, 500);
    });
});

Resize-event-listener menangkap semua panggilan resize yang masuk, membuat fungsi timeout untuk masing-masing panggilan dan menyimpan pengenal timeout bersama dengan nomor iterasi yang diawali dengan 'id-'(untuk dapat digunakan sebagai kunci array) di tos-array.

setiap kali, timout terpicu, ia memanggil fn-fungsi, yang memeriksa, apakah itu waktu tunggu terakhir dalam toslarik (fungsi fn menghapus setiap waktu tunggu yang dieksekusi). jika true (= if(len-1 == 0)), pengubahan ukuran selesai.

lsblsb.dll
sumber
Alangkah baiknya jika Anda memiliki beberapa komentar atau penjelasan tentang apa yang Anda lakukan di sini
Brett Gregson
1

jQuery menyediakan offmetode untuk menghapus event handler

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
Qiang
sumber