jQuery:如何仅在完成大小调整后调用RESIZE事件?


109

浏览器窗口调整大小后如何调用函数?

我正在尝试这样做,但是遇到了问题。我正在使用JQuery Resize事件函数:

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

但是,如果用户手动调整浏览器窗口的大小,则将连续调用此函数。这意味着,它可能会在很短的时间间隔内多次调用此函数。

如何仅一次调用调整大小函数(一旦浏览器窗口完成大小调整)?

更新

也不必使用全局变量。


@BGerrissen,如果您可以向我展示如何在不使用全局变量的情况下进行jsfiddle.net/Zevan/c9UE5/1,我肯定会:)
nickb 2010年

上面的cleartimeout / settimeout方法效果很好。
kris-o3

Answers:


129

这是使用thejh指令的示例

您可以将参考ID存储到任何setInterval或setTimeout。像这样:

var loop = setInterval(func, 30);

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

我喜欢这段代码是如此简单。有什么方法可以使这项工作无需使用全局变量?
尼克

如果我在没有全局变量的情况下如何做到这一点,那么我将得出“可接受的答案”
nickb 2010年

没问题。有几种方法。我已经编辑了答案,以反映我能想到的最简单的答案。
Zevan 2010年

感谢您的更新,但它似乎仍然使用全局变量。您可以更新它以不需要全局变量(变量ID)。谢谢
nickb

4
我认为您错过了帖子末尾的链接...请查看:jsfiddle.net/Zevan/c9UE5/5
Zevan 2010年

85

防抖

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

请注意,您可以将这种方法用于任何想要消除抖动的事件(按键事件等)。

调整超时参数以获得最佳期望效果。


1
我只是尝试运行它,它似乎运行TWICE。我将“ //做东西”替换为“ $(“ body”)。append(“ <br/> DONE!”);“ 并在调整浏览器大小时称其为TWICE
尼克

糟糕...我忘了一个相当重要的部分(实际上是设置了timeoutID)...现在已修复,请重试;)
BGerrissen

3
@ user43493:它调用cuntion func,内部this指针指向scope,并使用Array.prototype.slice.call(args)(从中生成标准数组args)作为参数
Eric 2010年

4
这是可重用的抽象,因此您不必手动编码超时或自己跟踪全局timeoutID。您可以使用它来进行更多操作,而不仅仅是调整窗口大小;)例如,通过传递更高的超时参数来消除提交按钮的抖动。您可以选择代码较少的解决方案,但我建议您将此代码段保存在您的工具包中,稍后您将不胜感激;)
BGerrissen

2
如果您已经在使用该库,那么Underscore.js可以很好地实现此目的。underscorejs.org/#debounce
twmulloy

22

您可以使用setTimeout(),并clearTimeout()连同jQuery.data

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

更新资料

我写了一个扩展来增强jQuery的默认on(&bind)-event-handler。如果在给定时间间隔内未触发事件,则它将针对一个或多个事件的事件处理程序函数附加到所选元素。如果您只想在延迟(例如resize事件)之后才触发回调,则这很有用。 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));

像其他任何处理程序onbind-event处理程序一样使用它,除了可以在最后传递一个额外的参数外:

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

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


1
从字面上看,每天都做同样的Google搜索。并在同一页上找到这段代码。希望我能记住它哈哈。
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);
});

我最喜欢这个,但是为什么不在条件语句上使用大括号呢?我知道没有它们就可以使用,但是其他开发人员很难看;)
teewuane 2014年

7

只是添加到上面,由于滚动条的弹出和弹出,通常会发生不必要的调整大小事件,下面是一些避免这种情况的代码:

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

jsfiddle


5

Underscore.js有两种很棒的方法可以完成此任务:throttledebounce。即使您不使用Underscore,也请查看这些功能的来源。这是一个例子:

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

2

这是我的方法:

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捕获所有传入的resize调用,为每个调用创建一个超时函数,并将timeout-identifier以及一个迭代数'id-'(由其用作数组键)保存在tos

每次timout触发时,它都会调用fn-function,以检查是否是tos数组中的最后一个超时(fn函数会删除每个已执行的timout)。如果为true(= if(len-1 == 0)),则调整大小已完成。


如果您对在这里的工作有任何评论或解释,那就太好了
Brett Gregson

1

jQuery提供了off一种删除事件处理程序的方法

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.