如何等待'resize'事件的“结束”,然后再执行操作?

317

所以我目前使用类似以下的代码:

$(window).resize(function(){resizedw();});

但是这个函数在调整大小过程中会被多次调用。是否有可能在其结束时捕获一个事件?


也许可以使用 .one(),这样它只会在所有调整大小完成后执行,而不是一遍又一遍地执行? - Brad Christie
5
当用户手动调整窗口大小(通过拖动)时,调整大小事件将被多次调用,因此使用.one()并不会有效。 - jessegavin
以上代码中可以移除匿名函数,以简化和提高速度:$(window).resize(resizedw)。 - Fornost
这里有一个jQuery库:https://github.com/nielse63/jquery.resizeend - rugk
26个回答

608
您可以使用 setTimeout() clearTimeout() 函数。
function resizedw(){
    // Haven't resized in 100ms!
}

var doit;
window.onresize = function(){
  clearTimeout(doit);
  doit = setTimeout(resizedw, 100);
};

jsfiddle 上的代码示例。


9
这是一个很好的答案。它所做的与我推荐的插件一样,只是没有使用插件。 - jessegavin
16
这是一个非常简单的 去抖动(debounce)概念的实现(http://unscriptable.com/2009/03/20/debouncing-javascript-methods/)。Paul Irish(和其他人)提出了一种更高效的解决方案,它不处理“不必要”的调整大小事件:http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/。 - rmoestl
为什么JavaScript没有resizeend事件? - quemeful
1
这不是本质上相同的问题吗?你只是在重复调用其他东西,而不是你实际想要做的事情... - oldboy
1
使用@rmoestl提到的去抖动概念。完美地工作了。感谢提供Paul Irish智能调整大小实现的链接。 - chri3g91
显示剩余3条评论

201

我遵循以下建议取得了成功:http://forum.jquery.com/topic/the-resizeend-event

下面是代码,这样您就不必再浏览他的帖子链接和源码了:

var rtime;
var timeout = false;
var delta = 200;
$(window).resize(function() {
    rtime = new Date();
    if (timeout === false) {
        timeout = true;
        setTimeout(resizeend, delta);
    }
});

function resizeend() {
    if (new Date() - rtime < delta) {
        setTimeout(resizeend, delta);
    } else {
        timeout = false;
        alert('Done resizing');
    }               
}

感谢sime.vidas提供的代码!


2
有人可能想将日期更改为类似于new Date(-1E12)的形式 - 即JSLint警告不要使用00 - elundmark
谢谢elundmark。我已经切换了日期实例化以使用单个0;希望这不会引发投诉。 - Dolan Antenucci
@elundmark 或使用 + 操作。rtime: Date; .... if (+new Date() - +rtime < delta) 在 TypeScript 中,resizeend 函数应该是箭头函数,像这样 resizeend=()=>。因为在 resizeend 函数中,this 引用了 window 对象。 - Muhammet Can TONBUL

94

这是我根据 @Mark Coleman 的回答编写的代码:

$(window).resize(function() {
    clearTimeout(window.resizedFinished);
    window.resizedFinished = setTimeout(function(){
        console.log('Resized finished.');
    }, 250);
});

谢谢马克!


2
不错的方法。还在这里提到了(https://css-tricks.com/snippets/jquery/done-resizing-event/),与之不同的是没有对超级变量“window”进行修改。 - Alwin Kesler
2
@AlwinKesler - 在你的例子中,变量resizeTimer是一个全局变量,这意味着它被定义在window上,所以它与此处完全相同,只是这个例子更好,因为你不需要在外部定义变量。将此变量添加到window对象也是有意义的,因为事件监听器绑定到的就是该对象。 - vsync
1
谢谢!只是想补充一下,在某些情况下,回调函数执行某些任务需要更长的时间间隔。例如,在我的情况下,250不起作用,但700效果很好。 - Maria Blair
1
最好的解决方案。 - Daniel Dewhurst

43

Internet Explorer 提供了一个 resizeEnd 事件。在你调整大小时,其他浏览器会多次触发 resize 事件。

这里还有其他很好的答案,展示了如何使用 setTimeout 和 lodash、underscore 的 .throttle.debounce 方法,因此我将提到 Ben Alman 的 throttle-debounce jQuery 插件,它可以实现您想要的功能。

假设您有这个函数,您想在调整大小后触发它:

function onResize() {
  console.log("Resize just happened!");
};

节流示例
在下面的示例中,onResize()函数只会在窗口调整大小期间每250毫秒被调用一次。

$(window).resize( $.throttle( 250, onResize) );

防抖示例
在下面的示例中,onResize()只会在窗口调整大小操作结束时被调用一次。这实现了与@Mark在他的回答中提出的相同结果。

$(window).resize( $.debounce( 250, onResize) );

1
Lodash 在这里也很有用,它有 _.throttle 和 _.debounce 方法。我认为 debounce 相对于上面接受的例子是一种更优越的方法。 - Kevinleary.net
1
是的,这个答案是在5年前写的。自那时起,jQuery插件已经发生了很多变化。这里也有一个独立的防抖函数 https://davidwalsh.name/javascript-debounce-function - jessegavin
值得一提的是,审计功能也非常重要。它可以像防抖功能一样进行操作,但还具有额外的特点:它还会确保在每个 X 毫秒之间触发,这在“漫长”的调整大小过程中非常棒,因为它保证网页在调整大小时仍然具有响应性(很赞!) - XDS

28

1
只想补充一下,lodash 也提供了这个功能。 - vsync

22

有一种更简单的方法,在调整大小结束时执行函数,而不是计算两次调用之间的时间差,只需这样做:

var resizeId;
$(window).resize(function() {
    clearTimeout(resizeId);
    resizeId = setTimeout(resizedEnded, 500);
});

function resizedEnded(){
    ...
}

对于Angular2的等效方法:

private resizeId;
@HostListener('window:resize', ['$event'])
onResized(event: Event) {
  clearTimeout(this.resizeId);
  this.resizeId = setTimeout(() => {
    // Your callback method here.
  }, 500);
}

对于角度方法,请在setTimeout中使用() => { }符号以保留作用域,否则您将无法进行任何函数调用或使用this


16

一种解决方法是使用一个函数扩展jQuery,例如:resized

$.fn.resized = function (callback, timeout) {
    $(this).resize(function () {
        var $this = $(this);
        if ($this.data('resizeTimeout')) {
            clearTimeout($this.data('resizeTimeout'));
        }
        $this.data('resizeTimeout', setTimeout(callback, timeout));
    });
};

示例用法:

$(window).resized(myHandler, 300);


很棒的解决方案。谢谢你。如何在每个 $(window).resized(); 上使用它?因为当我尝试使用多个 $(window).resized(); 时,只有最后一个被执行。 - Jandon
这个函数的工作方式是将数据保存在特定的作用域中,因此您可以在不同的作用域中调用它,而不只是在 window 中。$(scope).resized(yourHandler, 300)。或者通过实现一种将所有回调保存到数组中并在超时被触发时依次调用它们的方法。 - Jone Polvora

7

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

var loop = setInterval(func, 30);

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

为了不使用“全局”变量,你可以在函数本身中添加一个局部变量。例如:

$(window).resize(function() {
    clearTimeout(this.id);
    this.id = setTimeout(doneResizing, 500);
});

function doneResizing(){
  $("body").append("<br/>done!");   
}

4

您可以将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)事件处理器。如果事件在给定时间间隔内没有触发,则它将为一个或多个事件的选定元素附加一个事件处理函数。这对于仅在延迟后触发回调的情况(如调整大小事件)非常有用。 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事件处理程序一样使用它,除了你可以作为最后一个传递额外的参数:

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

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


3

ResizeStartResizeEnd窗口事件

http://jsfiddle.net/04fLy8t4/

我实现了一个函数,触发用户DOM元素上的两个事件:

  1. resizestart - 开始调整大小
  2. resizeend - 结束调整大小

代码:

var resizeEventsTrigger = (function () {
    function triggerResizeStart($el) {
        $el.trigger('resizestart');
        isStart = !isStart;
    }

    function triggerResizeEnd($el) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function () {
            $el.trigger('resizeend');
            isStart = !isStart;
        }, delay);
    }

    var isStart = true;
    var delay = 200;
    var timeoutId;

    return function ($el) {
        isStart ? triggerResizeStart($el) : triggerResizeEnd($el);
    };

})();

$("#my").on('resizestart', function () {
    console.log('resize start');
});
$("#my").on('resizeend', function () {
    console.log('resize end');
});

window.onresize = function () {
    resizeEventsTrigger( $("#my") );
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接