如何等待“调整大小”事件的“结束”然后才执行操作?

Posted

技术标签:

【中文标题】如何等待“调整大小”事件的“结束”然后才执行操作?【英文标题】:How to wait for the 'end' of 'resize' event and only then perform an action? 【发布时间】:2011-07-26 06:52:12 【问题描述】:

所以我目前使用类似的东西:

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

但是在调整大小过程进行时,这会被多次调用。是否可以在事件结束时捕获事件?

【问题讨论】:

可能使用.one() 附加,所以它只在所有调整大小完成后才执行,而不是一遍又一遍? 当用户手动(通过拖动)调整窗口大小时,resize 事件将被多次调用,因此使用 .one() 确实无效。 为了简单和边际快速,可以删除上面对匿名函数的使用:$(window).resize(resizedw) 这是一个 jQuery 库:github.com/nielse63/jquery.resizeend 【参考方案1】:

您可以使用setTimeout()clearTimeout()

function resizedw()
    // Haven't resized in 100ms!


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

jsfiddle 上的代码示例。

【讨论】:

这是一个很好的答案。它做了我推荐的插件所做的事情,只是没有插件。 我认为真正改善这一点的唯一方法是检测鼠标移动。不过,我怀疑深入研究它不会有回报。 这是否仅在调整大小在一秒钟内完成时才有效?当我尝试使用它时,我的功能正在触发(虽然我的窗口调整大小很慢) @MichaelHaren 由于调整大小的句柄通常在$(document) 之外,因此鼠标检测将仅限于运行 Microsoft Windows 及其 Internet Explorer 易受攻击版本的用户:iedataleak.spider.io/demo 这是 debounce 概念的一个非常简单的实现 (unscriptable.com/2009/03/20/debouncing-javascript-methods)。 Paul Irish(和其他人)提出了一种更有效的解决方案,它不处理“不必要的”调整大小事件:paulirish.com/2009/throttled-smartresize-jquery-event-handler【参考方案2】:

我很幸运得到了以下推荐: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 提供代码!

【讨论】:

人们可能希望将日期更改为 new Date(-1E12) 之类的东西 - 即 JSLint 警告使用 00 感谢 elundmark。我已将日期实例化切换为使用单个 0;希望这不会引起投诉。 @elundmark 或使用 + 操作。 rtime: Date; .... if (+new Date() - +rtime &lt; delta) 和打字稿中的 resizeend 函数应该是像 resizeend=()=&gt; 这样的箭头函数。因为在resizeend函数中,this引用了window对象。【参考方案3】:

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

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

谢谢马克!

【讨论】:

不错的方法。还提到了here,区别在于没有对超级变量window进行修改。 @AlwinKesler - 在你的例子中,变量resizeTimer 是一个全局变量,这意味着它没有定义window,所以它和这里完全一样,只有这个例子更好,因为你没有' t 需要在外部定义变量。并且将此变量添加到window 对象也很有意义,因为这是事件侦听器绑定到的对象。 谢谢!只是想补充一点,在某些情况下,在回调中执行某些任务需要更长的时间间隔。例如。就我而言,250 没有用,但 700 效果很好。 最好的解决方案。【参考方案4】:

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

这里还有其他很好的答案,展示了如何使用 setTimeout 以及来自 lodash 和下划线的 .throttle、.debounce 方法,所以我会提到 Ben Alman 的 throttle-debounce jQuery plugin,它完成了你的工作'在追求。

假设你有一个想要在调整大小后触发的函数:

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

油门示例 在以下示例中,onResize() 在窗口调整大小期间仅每 250 毫秒调用一次。

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

去抖动示例 在以下示例中,onResize() 只会在窗口大小调整操作结束时调用一次。这达到了@Mark 在他的回答中提出的相同结果。

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

【讨论】:

Lodash 在这里也很有用,它也有 _.throttle 和 _.debounce 方法。与上述公认的示例相比,我认为去抖动是一种更好的方法。 是的,这个答案是 5 年前写的。自 jQuery 插件时代以来发生了很多事情。这也是一个独立的去抖功能davidwalsh.name/javascript-debounce-function 值得一提的是审计。它做了 debounce 的额外改动:它会确保在每 X 毫秒之间触发,这在“长时间”调整大小期间非常有用,因为它保证网页即使在调整大小期间也会响应(neato!)【参考方案5】:

使用Underscore.js 有一个优雅的解决方案因此,如果您在项目中使用它,您可以执行以下操作 -

$( window ).resize( _.debounce( resizedw, 500 ) );

这应该足够了 :) 但是,如果您有兴趣阅读更多内容,可以查看我的博文 - http://rifatnabi.com/post/detect-end-of-jquery-resize-event-using-underscore-debounce(deadlink)

【讨论】:

只是想补充一点lodash也提供这个【参考方案6】:

一种解决方案是使用函数扩展 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);

【讨论】:

【参考方案7】:

在调整大小结束时执行函数比计算两次调用之间的时间间隔要简单得多,只需这样做:

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 中使用() =&gt; 表示法来保留作用域,否则您将无法进行任何函数调用或使用this

【讨论】:

【参考方案8】:

您可以将参考 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!");   

【讨论】:

【参考方案9】:

您可以将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。如果在给定的时间间隔内未触发事件,它会将一个或多个事件的事件处理函数附加到所选元素。如果您只想在延迟后触发回调,这很有用,例如调整大小事件,或者其他。 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/

【讨论】:

【参考方案10】:

这是对上面 Dolan 代码的修改,我添加了一个功能,它在调整大小开始时检查窗口大小并将其与调整结束时的大小进行比较,如果大小大于或小于边距(例如 1000)然后重新加载。

var rtime = new Date(1, 1, 2000, 12,00,00);
var timeout = false;
var delta = 200;
var windowsize = $window.width();
var windowsizeInitial = $window.width();

$(window).on('resize',function() 
    windowsize = $window.width();
    rtime = new Date();
    if (timeout === false) 
            timeout = true;
            setTimeout(resizeend, delta);
        
);

function resizeend() 
if (new Date() - rtime < delta) 
    setTimeout(resizeend, delta);
    return false;
 else 
        if (windowsizeInitial > 1000 && windowsize > 1000 ) 
            setTimeout(resizeend, delta);
            return false;
        
        if (windowsizeInitial < 1001 && windowsize < 1001 ) 
            setTimeout(resizeend, delta);
            return false;
         else 
            timeout = false;
            location.reload();
        
    
    windowsizeInitial = $window.width();
    return false;

【讨论】:

【参考方案11】:

这是一个非常简单的脚本,用于触发窗口对象上的“resizestart”和“resizeend”事件。

没有必要纠结日期和时间。

d 变量表示在触发调整大小结束事件之前调整大小事件之间的毫秒数,您可以使用它来更改结束事件的敏感度。

要收听这些事件,您只需:

resizestart:$(window).on('resizestart', function(event)console.log('Resize Start!'););

调整大小结束: $(window).on('resizeend', function(event)console.log('Resize End!'););

(function ($) 
    var d = 250, t = null, e = null, h, r = false;

    h = function () 
        r = false;
        $(window).trigger('resizeend', e);
    ;

    $(window).on('resize', function (event) 
        e = event || e;
        clearTimeout(t);

        if (!r) 
            $(window).trigger('resizestart', e);
            r = true;
        

        t = setTimeout(h, d);
    );
(jQuery));

【讨论】:

我需要调整大小的开始和结束,这看起来效果很好(在 Chrome、FF、Opera 和 IE11 中测试)。为了测试,我使用您的解决方案创建了一个 JSFiddle:jsfiddle.net/8fsn2joj【参考方案12】:

Mark Coleman 的答案肯定比选定的答案好得多,但如果您想避免超时 ID 的全局变量(Mark 的答案中的 doit 变量),您可以执行以下操作之一:

(1) 使用立即调用的函数表达式 (IIFE) 创建闭包。

$(window).resize((function()  // This function is immediately invoked
                               // and returns the closure function.
    var timeoutId;
    return function() 
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() 
            timeoutId = null; // You could leave this line out.
            // Code to execute on resize goes here.
        , 100);
    ;
)());

(2) 使用事件处理函数的属性。

$(window).resize(function() 
    var thisFunction = arguments.callee;
    clearTimeout(thisFunction.timeoutId);
    thisFunction.timeoutId = setTimeout(function() 
        thisFunction.timeoutId = null; // You could leave this line out.
        // Code to execute on resize goes here.
    , 100);
);

【讨论】:

选项 2,使用 arguments.callee,如果函数是从 ES6 转译的,将不起作用。【参考方案13】:

ResizeStartResizeEnd 窗口事件

http://jsfiddle.net/04fLy8t4/

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

    resizestart 调整大小结束

代码:

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

【讨论】:

【参考方案14】:

我自己写了一个小包装函数...

onResize  =   function(fn) 
    if(!fn || typeof fn != 'function')
        return 0;

    var args    = Array.prototype.slice.call(arguments, 1);

    onResize.fnArr    = onResize.fnArr || [];
    onResize.fnArr.push([fn, args]);

    onResize.loop   = function() 
        $.each(onResize.fnArr, function(index, fnWithArgs) 
            fnWithArgs[0].apply(undefined, fnWithArgs[1]);
        );
    ;

    $(window).on('resize', function(e) 
        window.clearTimeout(onResize.timeout);
        onResize.timeout    = window.setTimeout("onResize.loop();", 300);
    );
;

用法如下:

var testFn  = function(arg1, arg2) 
    console.log('[testFn] arg1: '+arg1);
    console.log('[testFn] arg2: '+arg2);
;

// document ready
$(function() 
    onResize(testFn, 'argument1', 'argument2');
);

【讨论】:

【参考方案15】:
(function()
    var special = jQuery.event.special,
        uid1 = 'D' + (+new Date()),
        uid2 = 'D' + (+new Date() + 1);

    special.resizestart = 
        setup: function() 
            var timer,
                handler =  function(evt) 
                    var _self = this,
                        _args = arguments;
                    if (timer) 
                        clearTimeout(timer);
                     else 
                        evt.type = 'resizestart';
                        jQuery.event.handle.apply(_self, _args);
                    

                    timer = setTimeout( function()
                        timer = null;
                    , special.resizestop.latency);
                ;
            jQuery(this).bind('resize', handler).data(uid1, handler);
        ,
        teardown: function()
            jQuery(this).unbind( 'resize', jQuery(this).data(uid1) );
        
    ;

    special.resizestop = 
        latency: 200,
        setup: function() 
            var timer,
                handler = function(evt) 
                    var _self = this,
                        _args = arguments;
                    if (timer) 
                        clearTimeout(timer);
                    
                    timer = setTimeout( function()
                        timer = null;
                        evt.type = 'resizestop';
                        jQuery.event.handle.apply(_self, _args);
                    , special.resizestop.latency);
                ;

            jQuery(this).bind('resize', handler).data(uid2, handler);
        ,
        teardown: function() 
            jQuery(this).unbind( 'resize', jQuery(this).data(uid2) );
        
    ;
)();

$(window).bind('resizestop',function()
    //...
);

【讨论】:

【参考方案16】:

好吧,就窗口管理器而言,每个调整大小事件都是它自己的消息,有明确的开始和结束,所以从技术上讲,每次调整窗口大小时,它结束.

话虽如此,也许您想为您的继续设置延迟? Here's an example.

var t = -1;
function doResize()

    document.write('resize');

$(document).ready(function()
    $(window).resize(function()
        clearTimeout(t);
        t = setTimeout(doResize, 1000);
    );
);

【讨论】:

【参考方案17】:

这是我用来延迟重复动作的,它可以在代码中的多个位置调用:

function debounce(func, wait, immediate) 
    var timeout;
    return function() 
        var context = this, args = arguments;
        var later = function() 
            timeout = null;
            if (!immediate) func.apply(context, args);
        ;
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    ;
;

用法:

$(window).resize(function ()  
   debounce(function() 
          //...
    , 500);
);

【讨论】:

【参考方案18】:

因为选择的答案实际上并没有起作用..如果你没有使用 jquery,这里有一个简单的节流函数,并带有一个如何使用它来调整窗口大小的示例

    function throttle(end,delta) 

    var base = this;

    base.wait = false;
    base.delta = 200;
    base.end = end;

    base.trigger = function(context) 

        //only allow if we aren't waiting for another event
        if ( !base.wait ) 

            //signal we already have a resize event
            base.wait = true;

            //if we are trying to resize and we 
            setTimeout(function() 

                //call the end function
                if(base.end) base.end.call(context);

                //reset the resize trigger
                base.wait = false;
            , base.delta);
        
    
;

var windowResize = new throttle(function() console.log('throttle resize');,200);

window.onresize = function(event) 
    windowResize.trigger();

【讨论】:

【参考方案19】:

这对我有用,因为我不想使用任何插件。

$(window).resize(function() 
    var originalWindowSize = 0;
    var currentWidth = 0;

    var setFn = function () 
        originalWindowSize = $(window).width();
    ;

    var checkFn = function () 
        setTimeout(function () 
            currentWidth = $(window).width();
            if (currentWidth === originalWindowSize) 
                console.info("same? = yes") 
                // execute code 
             else 
                console.info("same? = no"); 
                // do nothing 
            
        , 500)
    ;
    setFn();
    checkFn();
);

在窗口重新调整大小时调用“setFn”获取窗口宽度并保存为“originalWindowSize”。然后调用“checkFn”,它在 500 毫秒(或您的偏好)后获取当前窗口大小,并将原始窗口大小与当前窗口大小进行比较,如果它们不相同,则窗口仍在重新调整大小。不要忘记在生产中删除控制台消息,并且(可选)可以使“setFn”自动执行。

【讨论】:

【参考方案20】:
var resizeTimer;
$( window ).resize(function() 
    if(resizeTimer)
        clearTimeout(resizeTimer);
    
    resizeTimer = setTimeout(function() 
        //your code here
        resizeTimer = null;
        , 200);
    );

这适用于我在 chrome 中尝试做的事情。这将在最后一次调整大小事件后 200 毫秒后触发回调。

【讨论】:

【参考方案21】:

更新!

我还创造了更好的选择: https://***.com/a/23692008/2829600 (支持“删除功能”)

原帖:

我编写了这个简单的函数来处理执行延迟,在 jQuery .scroll() 和 .resize() 中很有用,所以 callback_f 对于特定的 id 字符串只会运行一次。

function delay_exec( id, wait_time, callback_f )

    // IF WAIT TIME IS NOT ENTERED IN FUNCTION CALL,
    // SET IT TO DEFAULT VALUE: 0.5 SECOND
    if( typeof wait_time === "undefined" )
        wait_time = 500;

    // CREATE GLOBAL ARRAY(IF ITS NOT ALREADY CREATED)
    // WHERE WE STORE CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID
    if( typeof window['delay_exec'] === "undefined" )
        window['delay_exec'] = [];

    // RESET CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID,
    // SO IN THAT WAY WE ARE SURE THAT callback_f WILL RUN ONLY ONE TIME
    // ( ON LATEST CALL ON delay_exec FUNCTION WITH SAME ID  )
    if( typeof window['delay_exec'][id] !== "undefined" )
        clearTimeout( window['delay_exec'][id] );

    // SET NEW TIMEOUT AND EXECUTE callback_f WHEN wait_time EXPIRES,
    // BUT ONLY IF THERE ISNT ANY MORE FUTURE CALLS ( IN wait_time PERIOD )
    // TO delay_exec FUNCTION WITH SAME ID AS CURRENT ONE
    window['delay_exec'][id] = setTimeout( callback_f , wait_time );



// USAGE

jQuery(window).resize(function() 

    delay_exec('test1', 1000, function()
        console.log('1st call to delay "test1" successfully executed!');
    );

    delay_exec('test1', 1000, function()
        console.log('2nd call to delay "test1" successfully executed!');
    );

    delay_exec('test1', 1000, function()
        console.log('3rd call to delay "test1" successfully executed!');
    );

    delay_exec('test2', 1000, function()
        console.log('1st call to delay "test2" successfully executed!');
    );

    delay_exec('test3', 1000, function()
        console.log('1st call to delay "test3" successfully executed!');
    );

);

/* RESULT
3rd call to delay "test1" successfully executed!
1st call to delay "test2" successfully executed!
1st call to delay "test3" successfully executed!
*/

【讨论】:

你能澄清一下这里的用法吗?您是否建议这样做:$(window).resize(function() delay_exec('test1', 30, function() ... delayed stuff here ... ); );?否则非常干净的代码。感谢分享。 :) 你摇滚!谢谢@Déján!一路+1。很酷的代码示例,根据我的测试,它运行良好。使用也很简单。再次感谢分享。 :)【参考方案22】:
var flag=true;
var timeloop;

$(window).resize(function()
    rtime=new Date();
    if(flag)
        flag=false;
        timeloop=setInterval(function()
            if(new Date()-rtime>100)
                myAction();
        ,100);
    
)
function myAction()
    clearInterval(timeloop);
    flag=true;
    //any other code...

【讨论】:

【参考方案23】:

我不知道我的代码是否适用于其他人,但这对我来说真的很棒。我通过分析 Dolan Antenucci 代码得到了这个想法,因为他的版本不适合我,我真的希望它对某人有所帮助。

var tranStatus = false;
$(window).resizeend(200, function()
    $(".cat-name, .category").removeAttr("style");
    //clearTimeout(homeResize);
    $("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) 
      tranStatus = true;
    );
    processResize();
);

function processResize()
  homeResize = setInterval(function()
    if(tranStatus===false)
        console.log("not yet");
        $("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) 
            tranStatus = true;
        ); 
    else
        text_height();
        clearInterval(homeResize);
    
  ,200);

【讨论】:

【参考方案24】:

我写了一个函数,当包装在任何调整大小事件中时,它会传递一个函数。它使用一个间隔,以便调整大小甚至不会不断创建超时事件。这允许它独立于调整大小事件执行,而不是应该在生产中删除的日志条目。

https://github.com/UniWrighte/resizeOnEnd/blob/master/resizeOnEnd.js

        $(window).resize(function()
            //call to resizeEnd function to execute function on resize end.
    //can be passed as function name or anonymous function
            resizeEnd(function()



    );

        );

        //global variables for reference outside of interval
        var interval = null;
        var width = $(window).width();
    var numi = 0; //can be removed in production
        function resizeEnd(functionCall)
            //check for null interval
            if(!interval)
                //set to new interval
                interval = setInterval(function()
        //get width to compare
                    width2 = $(window).width();
        //if stored width equals new width
                    if(width === width2)
                        //clear interval, set to null, and call passed function
                        clearInterval(interval);
                        interval = null; //precaution
                        functionCall();

                    
        //set width to compare on next interval after half a second
                    width = $(window).width();
                , 500);

            else
                //logging that should be removed in production
                console.log("function call " + numi++ + " and inteval set skipped");

            

【讨论】:

【参考方案25】:

我想我的情况可能与其他情况不同,但我只遇到了 ios 上的方向更改问题,但希望调整大小事件立即运行。我使用了ScreenOrientation API:

screen.orientation.addEventListener('change', (e) => );

【讨论】:

以上是关于如何等待“调整大小”事件的“结束”然后才执行操作?的主要内容,如果未能解决你的问题,请参考以下文章

如何实现线程互等,线程2等待线程1结束后才继续执行。(可设置标志位) 求源代码

Zookeeper应用场景之分布式屏障Barrier

Java中Component的componentResized事件,但仅在鼠标释放时执行

iOS UITableView reloadData 刷新结束后执行后续操作

怎么让当前线程等待另一个线程完成之后再去执行

如何实现java主线程等待子线程执行完毕之后再执行