Как дождаться окончания события 'resize' и только потом выполнить действие?


244

Поэтому я сейчас использую что-то вроде:

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

Но это вызывается много раз, пока продолжается процесс изменения размера. Можно ли поймать событие, когда оно заканчивается?


Может быть, присоединить с помощью, .one()так что он выполняется только после того, как все изменения размера сделано, а не снова и снова?
Брэд Кристи

5
Когда пользователь изменяет размер окна вручную (путем его перетаскивания), событие resize будет вызываться более одного раза, поэтому использование .one () действительно не будет эффективным.
jessegavin

Использование анонимной функции в выше могут быть удалены, для простоты и предельной быстроты: $ (окно) .resize (resizedw)
Форноста

Вот библиотека jQuery для этого: github.com/nielse63/jquery.resizeend
rugk

Ответы:


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
Возможно, вы захотите изменить дату на что-то вроде new Date(-1E12)- то есть JSLint предупреждает об использовании 00.
elundmark

Спасибо Elundmark. Я переключил инстанцирование даты, чтобы использовать одиночные 0; Надеюсь, что это не приведет к жалобе.
Долан Антенуччи

@elundmark или используйте + операция. rtime: Date; .... if (+new Date() - +rtime < delta)и в машинописной функции resizeend должна быть функция стрелки как это resizeend=()=>. Потому что в функции resizeend thisссылка на объект окна.
Мухаммет Кан ТОНБУЛ

517

Вы можете использовать setTimeout()иclearTimeout()

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

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

Пример кода на jsfiddle .


7
Это отличный ответ. Он делает то, что рекомендовал плагин, только без плагина.
jessegavin

Я думаю, что единственный способ действительно улучшить это, это обнаружить движение мыши. Я подозреваю, что копание в нем не окупится, хотя.
Майкл Харен

Это работает, только если изменение размера завершено в течение секунды? Моя функция запускалась, когда я пытался использовать это (хотя я медленно менял размер окна)
Dolan Antenucci,

@MichaelHaren Поскольку дескриптор изменения размера обычно находится за пределами $(document), обнаружение мыши будет ограничено пользователями, работающими под управлением Microsoft Windows и уязвимыми версиями его Internet Explorer: iedataleak.spider.io/demo
Alastair

12
Это очень простая реализация концепции debounce ( unscriptable.com/2009/03/20/debouncing-javascript-methods ). Пол Ирриш (и другие) представил гораздо более эффективное решение, которое не обрабатывает «ненужные» события изменения размера: paulirish.com/2009/throttled-smartresize-jquery-event-handler
rmoestl

78

Это код, который я пишу в соответствии с ответом @Mark Coleman:

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

Спасибо Марк!


1
Хороший приближенный. Также упоминается здесь с той разницей , что никакие изменений в супер переменные окна не производятся.
Алвин Кеслер

2
@AlwinKesler - в вашем примере переменная resizeTimerявляется глобальной переменной, то есть она определена без изменений window, поэтому она точно такая же, как здесь, только этот пример лучше, так как вам не нужно определять переменную извне. и также имеет смысл добавить эту переменную к windowобъекту, поскольку это объект, с которым связан прослушиватель событий.
Вс

1
Спасибо! Просто хотел добавить, что в некоторых случаях для выполнения определенных задач в обратном вызове требуется более длительный интервал времени. Например, в моем случае 250 не работало, но 700 работало отлично.
Мария Блэр

Самое прекрасное решение.
Дэниел Дьюхерст

36

Internet Explorer предоставляет событие resizeEnd . Другие браузеры будут вызывать событие изменения размера много раз во время изменения размера.

Здесь есть и другие замечательные ответы, которые показывают, как использовать setTimeout и .throttle ,Методы .debounce от lodash и underscore, поэтому я упомяну плагин jQuery Бена Алмана, который выполняет то, что вам нужно.

Предположим, у вас есть эта функция, которую вы хотите вызвать после изменения размера:

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

Пример Throttle
В следующем примере onResize()будет вызываться только один раз каждые 250 миллисекунд при изменении размера окна.

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

Пример Debounce
В следующем примере onResize()будет вызываться только один раз в конце действия изменения размера окна. Это достигает того же результата, который @Mark представляет в своем ответе.

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

1
Здесь также полезен Lodash, который также имеет методы _.throttle и _.debounce. Я думаю, что debounce - это превосходный подход по сравнению с принятым выше примером.
Кевин Лири

1
Да, этот ответ был написан более 5 лет назад. Многое произошло со дней плагинов jQuery. Вот отдельная функция debounce тоже davidwalsh.name/javascript-debounce-function
jessegavin

26

Существует элегантное решение с использованием Underscore.js. Итак, если вы используете его в своем проекте, вы можете сделать следующее:

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

Этого должно быть достаточно :) Но, если вы заинтересованы, чтобы узнать больше об этом, вы можете проверить мой блог - http://rifatnabi.com/post/detect-end-of-jquery-resize-event-using-underscore -debounce (мертвая ссылка)


1
Просто lodash
хочу

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

Вы можете сохранить ссылочный идентификатор для любого 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));
});

Обновить

Я написал расширение для улучшения стандартного on(& bind) -event-handler jQuery . Он прикрепляет функцию-обработчик события для одного или нескольких событий к выбранным элементам, если событие не было запущено в течение заданного интервала. Это полезно, если вы хотите запустить обратный вызов только после задержки, например, события изменения размера или в противном случае. 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));

Используйте его как любой другой обработчик onили bind-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);
}

Для углового метода используйте () => { }обозначение в, setTimeoutчтобы сохранить область, иначе вы не сможете выполнять какие-либо вызовы функций или использовать this.


2

Это модификация вышеприведенного кода Долана, я добавил функцию, которая проверяет размер окна в начале изменения размера и сравнивает его с размером в конце изменения размера, если размер больше или меньше поля ( например, 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

Ответ Марка Колемана, безусловно, намного лучше, чем выбранный ответ, но если вы хотите избежать глобальной переменной для идентификатора тайм-аута ( 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.
Мартин Берч

1

я написал небольшую упаковочную функцию самостоятельно ...

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

resizeend: $(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
Кейт, округ Колумбия,

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, вот простая функция throttle с примером того, как использовать его с изменением размера окна

    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 будет запускаться только один раз для конкретной строки идентификатора.

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

Ты жжешь! Спасибо @ Déján! +1 полностью. Классный пример кода, он работает очень хорошо из того, что я тестировал. Прост в использовании тоже. Еще раз спасибо за обмен. :)
mhulse

0

События ResizeStart и ResizeEnd для окна

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

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

Я написал функцию, которая передает функцию, когда обернута в любое событие изменения размера. Он использует интервал, так что даже изменение размера не всегда создает события тайм-аута. Это позволяет ему выполнять независимо от события изменения размера, кроме записи журнала, которая должна быть удалена в производственной среде.

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

            }

}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.