JQuery: Làm thế nào để gọi sự kiện RESIZE chỉ khi nó HOÀN TẤT thay đổi kích thước?


109

Làm cách nào để gọi một hàm sau khi cửa sổ trình duyệt đã HOÀN TẤT thay đổi kích thước?

Tôi đang cố gắng làm như vậy, nhưng gặp sự cố. Tôi đang sử dụng hàm sự kiện JQuery Resize:

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

Tuy nhiên, chức năng này được gọi liên tục nếu người dùng đang thay đổi kích thước cửa sổ trình duyệt theo cách thủ công. Có nghĩa là, nó có thể gọi hàm này hàng chục lần trong khoảng thời gian ngắn.

Làm thế nào tôi có thể gọi chức năng thay đổi kích thước chỉ một lần (sau khi cửa sổ trình duyệt đã hoàn thành việc thay đổi kích thước)?

CẬP NHẬT

Ngoài ra mà không cần phải sử dụng một biến toàn cục.


@BGerrissen, nếu bạn có thể chỉ cho tôi cách thực hiện jsfiddle.net/Zevan/c9UE5/1 mà không có biến toàn cục, tôi chắc chắn sẽ làm :)
nickb

phương pháp cleartimeout / settimeout ở trên hoạt động rất tốt.
kris-o3

Câu trả lời:


129

Đây là một ví dụ sử dụng hướng dẫn của thejh

Bạn có thể lưu trữ một id tham chiếu cho bất kỳ setInterval hoặc setTimeout nào. Như thế này:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);

Tôi thích rằng mã này rất đơn giản. Bất kỳ cách nào để làm cho điều này hoạt động mà không cần phải sử dụng một biến toàn cục?
nickb

Nếu tôi thấy làm thế nào để làm điều này mà không có sự biến toàn cầu, tôi sẽ làm cho rằng "câu trả lời chấp nhận"
nickb

không vấn đề gì. Có một số cách. Tôi đã chỉnh sửa câu trả lời để phản ánh câu trả lời đơn giản nhất mà tôi có thể nghĩ ra.
Zevan

cảm ơn bạn đã cập nhật nhưng có vẻ như nó vẫn sử dụng một biến toàn cục. Bạn có thể cập nhật nó để không yêu cầu biến toàn cục (var id) không. Cảm ơn
nickb

4
Tôi nghĩ rằng bạn đã bỏ lỡ liên kết ở cuối bài đăng ... hãy kiểm tra nó: 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 
} ) );

Lưu ý, bạn có thể sử dụng phương pháp này cho bất kỳ thứ gì bạn muốn xóa (sự kiện chính, v.v.).

Tinh chỉnh thông số thời gian chờ để có hiệu ứng mong muốn tối ưu.


1
Tôi vừa thử chạy cái này và nó có vẻ chạy TWICE . Tôi đã thay thế "// làm công việc" bằng "$ (" body "). Append (" <br/> DONE! ");" và nó gọi nó là TWICE trên một trình duyệt thay đổi kích thước
nickb

oops ... Tôi quên một phần khá quan trọng (trên thực tế thiết lập các timeoutID) ... cố định bây giờ, xin đừng thử lại;)
BGerrissen

3
@ user43493: Nó gọi là lồnion func, với thiscon trỏ bên trong trỏ đến scopevà với Array.prototype.slice.call(args)(tạo ra một mảng chuẩn ngoài args) là các đối số
Eric

4
Đây là phần trừu tượng có thể tái sử dụng, vì vậy bạn không cần phải viết tay các thời gian chờ hoặc tự theo dõi các timeoutID toàn cầu. Bạn có thể sử dụng nó cho nhiều cách khác, sau đó chỉ cần thay đổi kích thước cửa sổ;) chẳng hạn như để loại bỏ một nút gửi bằng cách chuyển tham số thời gian chờ cao hơn. Bạn có thể lựa chọn các giải pháp mã ít hơn, nhưng tôi khuyên bạn giữ đoạn mã này trong bộ tài liệu của bạn, bạn sẽ đánh giá cao nó sau này;)
BGerrissen

2
Underscore.js có một triển khai tuyệt vời của điều này nếu bạn đang sử dụng lib đó. underscorejs.org/#debounce
twmulloy

22

Bạn có thể sử dụng setTimeout()clearTimeout()kết hợp với jQuery.data:

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

Cập nhật

Tôi đã viết một tiện ích mở rộng để nâng cao trình xử lý mặc định on(& bind) -event- của jQuery . Nó đính kèm một hàm xử lý sự kiện cho một hoặc nhiều sự kiện vào các phần tử đã chọn nếu sự kiện không được kích hoạt trong một khoảng thời gian nhất định. Điều này hữu ích nếu bạn muốn kích hoạt một cuộc gọi lại chỉ sau một khoảng thời gian trễ, chẳng hạn như sự kiện thay đổi kích thước hoặc các trường hợp khác. 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));

Sử dụng nó giống như bất kỳ trình xử lý onhoặc bind-event nào khác , ngoại trừ việc bạn có thể chuyển một tham số bổ sung vào cuối cùng:

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

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


1
Theo nghĩa đen, thực hiện cùng một tìm kiếm google mỗi ngày. và đến cùng một trang cho đoạn mã này. Ước gì tôi có thể nhớ nó 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);
});

Tôi thích điều này nhất nhưng tại sao không sử dụng dấu ngoặc nhọn trên các câu lệnh điều kiện của bạn? Tôi biết nó hoạt động mà không có họ nhưng đó là một nỗi đau cho các nhà phát triển khác để xem;)
teewuane

7

Chỉ để thêm vào những điều trên, thường xảy ra các sự kiện thay đổi kích thước không mong muốn do các thanh cuộn bật ra và bật ra, đây là một số mã để tránh điều đó:

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

xem jsfiddle


5

Underscore.js có một số phương pháp tuyệt vời cho tác vụ này: throttledebounce. Ngay cả khi bạn không sử dụng Dấu gạch dưới, hãy xem nguồn của các hàm này. Đây là một ví dụ:

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

2

Đây là cách tiếp cận của tôi:

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

Trình nghe thay đổi kích thước-sự kiện bắt tất cả các lệnh gọi thay đổi kích thước đến, tạo một hàm thời gian chờ cho mỗi lệnh và lưu mã định danh thời gian chờ cùng với một số lặp được thêm vào trước 'id-'(có thể sử dụng làm khóa mảng) trong-mảng tos.

mỗi lần, timout kích hoạt, nó gọi -function, sẽ fnkiểm tra, nếu đó là lần hết thời gian cuối cùng trong tosmảng (fn-function sẽ xóa mọi timout được thực thi). nếu true (= if(len-1 == 0)), quá trình thay đổi kích thước đã hoàn tất.


Nó sẽ được tốt đẹp nếu bạn có một số ý kiến hoặc giải thích về những gì bạn đang làm gì ở đây
Brett Gregson

1

jQuery cung cấp một offphương thức để loại bỏ trình xử lý sự kiện

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.