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


243

所以我目前使用类似:

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

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


也许附加使用,.one()以便仅在完成所有调整大小后才执行,而不是一遍又一遍?
布拉德·克里斯蒂

5
当用户手动调整窗口大小(通过拖动窗口)时,将多次调用resize事件,因此使用.one()确实无效。
jessegavin 2011年

为了简单和边际快速起见,可以删除上面使用的匿名函数:$(window).resize(resizedw)
Fornost,

这是一个为此的jQuery库:github.com/nielse63/jquery.resizeend
rug

Answers:


177

我很满意以下建议: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的代码!


1
可能需要将Date更改为类似的内容new Date(-1E12)-即JSLint警告有关使用的信息00
elundmark

感谢elundmark。我已将日期实例化切换为使用单0。希望不会引起投诉。
Dolan Antenucci 2015年

@elundmark或使用+操作。rtime: Date; .... if (+new Date() - +rtime < delta)在typescript中,resizeend函数应该是这样的箭头函数resizeend=()=>。因为在resizeend函数中,this引用窗口对象。
Muhammet Can TONBUL

517

您可以使用setTimeout()clearTimeout()

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

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

jsfiddle上的代码示例。


7
这是一个很好的答案。它执行我推荐的插件所做的事情,只是没有插件。
jessegavin 2011年

我认为真正改善此问题的唯一方法是检测鼠标的移动。我怀疑深入研究不会取得回报。
Michael Haren

仅在调整大小在一秒钟内完成时,这才起作用吗?当我尝试使用此功能时,我的功能正在触发(虽然我的窗口调整大小后很慢)
Dolan Antenucci

@MichaelHaren由于调整大小手柄通常不在的范围内$(document),因此鼠标检测将仅限于运行Microsoft Windows及其Internet Explorer漏洞版本的用户:iedataleak.spider.io/demo
Alastair

12
这是去抖动概念的非常简单的实现(unscriptable.com/2009/03/20/debouncing-javascript-methods)。保罗·爱尔兰(及其他人)提出了一种更有效的解决方案,该解决方案不处理“不必要的”调整大小事件:paulirish.com/2009/throttled-smartresize-jquery-event-handler
rmoestl 2015年

78

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

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

谢谢马克!


1
好的批准。这里也提到了区别,即不对超变量窗口进行任何修改。
阿尔温·凯斯勒

2
@AlwinKesler-在您的示例中,变量resizeTimer是全局变量,表示未定义window,因此与此处完全相同,仅此示例更好,因为您无需在外部定义变量。并且将此变量添加到window对象也很有意义,因为这是事件侦听器绑定到的对象。
vsync

1
谢谢!只是想补充一点,在某些情况下,需要更长的时间间隔才能在回调中执行某些任务。例如,以我为例,有250个无效,但有700个有效。
玛丽亚·布莱尔

最好的解决方案。
丹尼尔·德赫斯特

36

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

这里还有其他一些很棒的答案,显示了如何使用setTimeout和.throttlelodash和下划线的.debounce方法,因此,我将提及Ben Alman的节流防抖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方法。与上面接受的示例相比,我认为去抖动是一种更好的方法。
凯文·里里

1
是的,这个答案是5年前写的。自从jQuery插件时代以来,发生了很多事情。这也是一个独立的反跳功能davidwalsh.name/javascript-debounce-function
jessegavin


10

一种解决方案是使用功能扩展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

您可以将参考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)-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/


3

与计算两次调用之间的时间差相比,在调整大小的末尾执行函数要简单得多,方法如下:

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

对于angular方法,请使用中的() => { }表示法setTimeout保留范围,否则将无法进行任何函数调用或使用this


2

这是对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;
}

2

马克·科尔曼(Mark Coleman)的答案肯定比所选答案好得多,但是如果您要避免超时ID( doit Mark答案中变量),可以执行以下操作之一:

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

如果从ES6编译函数,则使用arguments.callee的选项2将不起作用。
Martin Burch

1

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

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

1
(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(){
    //...
});

1

嗯,就窗口管理器而言,每个调整大小事件都是其自己的消息,具有不同的开始和结束,因此从技术上讲,每次调整窗口大小时,它都是结束。

话虽如此,也许您想为续约设定一个延迟?这是一个例子。

var t = -1;
function doResize()
{
    document.write('resize');
}
$(document).ready(function(){
    $(window).resize(function(){
        clearTimeout(t);
        t = setTimeout(doResize, 1000);
    });
});

1

这是非常简单的脚本,可触发窗口对象上的“ 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));

1
我需要调整大小的开始和结束,并且看起来工作正常(在Chrome,FF,Opera和IE11中进行了测试)。为了测试,我用您的解决方案创建了一个JSFiddle:jsfiddle.net/8fsn2joj
Keith DC

1

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

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

0

因为选择的答案实际上没有用..并且,如果您不使用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();
}

0

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

$(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”自动执行。


0
var resizeTimer;
$( window ).resize(function() {
    if(resizeTimer){
        clearTimeout(resizeTimer);
    }
    resizeTimer = setTimeout(function() {
        //your code here
        resizeTimer = null;
        }, 200);
    });

这对于我在Chrome中尝试做的事情很有用。直到上一次调整大小事件之后200毫秒,这才会触发回调。


0

更新!

我也创建的更好的替代方法是在这里: https : //stackoverflow.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 ... }); });?否则代码很干净。感谢分享。:)
mhulse 2014年

你摇滚!谢谢@Déján!一路+1。很酷的代码示例,根据我的测试,它可以很好地工作。也易于使用。再次感谢您的分享。:)
mhulse

0

窗口的ResizeStartResizeEnd事件

http://jsfiddle.net/04fLy8t4/

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

  1. resizestart
  2. 调整大小

码:

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

0
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...
}

0

我不知道我的代码是否适用于其他人,但对我来说确实做得很好。我通过分析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);
}

0

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

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

            }

}

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.