JavaScript / JQuery: $ (window) .resize, как запустить ПОСЛЕ изменения размера?

235

Я использую JQuery как таковой:

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

Однако, похоже, что если человек вручную изменяет размеры своих окон браузера, перетаскивая край окна, чтобы сделать его больше / меньше, .resizeсобытие выше запускается несколько раз.

Вопрос: Как вызвать функцию ПОСЛЕ изменения размера окна браузера (чтобы событие срабатывало только один раз)?

Сара
источник
См. Этот ответ: stackoverflow.com/questions/667426/… Это предполагает использование тайм-аутов для задержки выполнения вашей функции.
Аластер Питтс
Я не знаю, если это возможно, вы можете попробовать этот плагин, хотя benalman.com/projects/jquery-resize-plugin
BrunoLM
Кстати, я заметил, что это очень полезно в мобильных браузерах, которые имеют тенденцию постепенно скрывать адресную строку, когда пользователь прокручивает страницу, поэтому изменяя размер экрана. Я также ожидал, что изменение размера экрана произойдет только на рабочем столе ...
MakisH

Ответы:

299

Вот модификация решения CMS, которая может быть вызвана в нескольких местах в вашем коде:

var waitForFinalEvent = (function () {
  var timers = {};
  return function (callback, ms, uniqueId) {
    if (!uniqueId) {
      uniqueId = "Don't call this twice without a uniqueId";
    }
    if (timers[uniqueId]) {
      clearTimeout (timers[uniqueId]);
    }
    timers[uniqueId] = setTimeout(callback, ms);
  };
})();

Использование:

$(window).resize(function () {
    waitForFinalEvent(function(){
      alert('Resize...');
      //...
    }, 500, "some unique string");
});

Решение CMS хорошо, если вы вызываете его только один раз, но если вы вызываете его несколько раз, например, если разные части вашего кода настраивают отдельные обратные вызовы для изменения размера окна, то он потерпит неудачу, потому что они совместно используют timerпеременную.

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

Brahn
источник
2
Я <3 u, я комбинировал это, изменяя размеры полноэкранных (не html5) графиков Highcharts и прекрасно работает.
Майкл Дж. Калкинс
Абсолютно невероятно!
Starkers
2
Поскольку ваш ответ мне очень помог, я подумал, что поделился рабочим прототипом предоставленного вами кода для остальной части сообщества: jsfiddle.net/h6h2pzpu/3 . Наслаждайтесь всеми!
Йонас Ставски
1
Это все идеально, НО задерживает выполнение фактической функции на количество миллисекунд (мс), что может быть ОЧЕНЬ НЕОБХОДИМО.
Томас М
Это просто прекрасно, есть ли способ получить последнее изменение размера и не нужно передавать эти миллисекунды? В любом случае это просто спасло мой день: ') Спасибо.
Лев
138

Я предпочитаю создать событие:

$(window).bind('resizeEnd', function() {
    //do something, window hasn't changed size in 500ms
});

Вот как вы его создаете:

 $(window).resize(function() {
        if(this.resizeTO) clearTimeout(this.resizeTO);
        this.resizeTO = setTimeout(function() {
            $(this).trigger('resizeEnd');
        }, 500);
    });

Это может быть где-то в глобальном файле JavaScript.

Карлос Мартинес Т
источник
Я использовал это решение. Но в долгосрочной перспективе я хотел бы реализовать то же, что и решение Брахна.
Бхарат Патил
это позволяет вам связывать это событие повсюду, а не только в одной функции, при условии, что у вас есть несколько страниц / файлов .js, требующих отдельной реакции на него
Hanzolo
Это не сработало для меня, но ответ
DusanV сработал
123

Я использую следующую функцию для отсрочки повторных действий, она будет работать для вашего случая:

var delay = (function(){
  var timer = 0;
  return function(callback, ms){
    clearTimeout (timer);
    timer = setTimeout(callback, ms);
  };
})();

Использование:

$(window).resize(function() {
    delay(function(){
      alert('Resize...');
      //...
    }, 500);
});

Функция обратного вызова, переданная ей, будет выполняться только в том случае, если по истечении указанного промежутка времени был сделан последний вызов delay, в противном случае таймер будет сброшен, я считаю это полезным для других целей, таких как обнаружение того, когда пользователь прекратил печатать, и т. Д. ..

CMS
источник
5
Я думаю, что этот подход может потерпеть неудачу, если используется несколько раз (например, если различные части обратных вызовов установки кода $(window).resize), потому что они все будут совместно использовать timerпеременную. Смотрите мой ответ ниже для предлагаемого решения.
Brahn
Очень хорошо! Работает как шарм! Нравится ваши ответы ... просто и элегантно!
Мистер Тыква
74

Если у вас установлен Underscore.js, вы можете:

$(window).resize(_.debounce(function(){
    alert("Resized");
},500));
JT.
источник
1
Даже если вы не хотите включать Underscore, по крайней мере возьмите источник для этого: underscorejs.org/docs/underscore.html#section-67
tybro0103
Дебад - это правильная техника, это просто вопрос реализации. И это лучшая реализация, если Underscore / Lodash уже является зависимостью проекта.
Фактор Мистик
Это то, что я сейчас использую,
Бхарат Патил,
20

Некоторые из ранее упомянутых решений не работают для меня, хотя они имеют более общее использование. В качестве альтернативы я нашел этот, который сделал работу по изменению размера окна:

$(window).bind('resize', function(e){
    window.resizeEvt;
    $(window).resize(function(){
        clearTimeout(window.resizeEvt);
        window.resizeEvt = setTimeout(function(){
        //code to do after window is resized
        }, 250);
    });
});
DusanV
источник
1
Только твой пример сработал для меня ... другие выше не сработали! Я не уверен, почему ваш ответ не был выбран.
Мамбо Джамбо
Я не понимаю, зачем перехватывать событие resize внутри события resize, но оно работает и для меня ...
lightbyte
Mumbo Jumbo, я думаю, потому что другие сосредотачиваются на обобщении, ища решение любой такой проблемы (многократные вызовы функции).
DusanV
lightbyte, потому что вы должны останавливать предыдущие события каждый раз, когда вызывается функция.
DusanV
13

Большое спасибо Дэвиду Уолшу, вот ванильная версия подчеркивания.

Код:

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
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);
    };
};

Простое использование:

var myEfficientFn = debounce(function() {
    // All the taxing stuff you do
}, 250);

$(window).on('resize', myEfficientFn);

Ссылка: http://davidwalsh.name/javascript-debounce-function

Ирвин Доминин
источник
6

На самом деле, как я знаю, вы не можете выполнять некоторые действия именно тогда, когда изменение размера отключено, просто потому, что вы не знаете действий будущего пользователя. Но вы можете предположить, что прошло время между двумя событиями изменения размера, поэтому, если вы ждете немного больше, чем это время, и изменение не производится, вы можете вызвать свою функцию.
Идея в том, что мы используем setTimeoutего и его идентификатор, чтобы сохранить или удалить его. Например, мы знаем, что время между двумя событиями изменения размера составляет 500 мс, поэтому мы будем ждать 750 мс.

var a;
$(window).resize(function(){
  clearTimeout(a);
  a = setTimeout(function(){
    // call your function
  },750);
});

newint
источник
3

Объявите глобально задержанный слушатель:

var resize_timeout;

$(window).on('resize orientationchange', function(){
    clearTimeout(resize_timeout);

    resize_timeout = setTimeout(function(){
        $(window).trigger('resized');
    }, 250);
});

А ниже используйте слушателей resizedсобытия как хотите:

$(window).on('resized', function(){
    console.log('resized');
});
gzzz
источник
Спасибо, это лучшее решение на данный момент. Также я проверил, что 250ms работает лучше всего при изменении размера окна.
AlexioVay
2

Простой плагин jQuery для отложенного изменения размера окна.

СИНТАКСИС:

Добавить новую функцию для изменения размера события

jQuery(window).resizeDelayed( func, delay, id ); // delay and id are optional

Удалить функцию (объявив ее идентификатор), добавленную ранее

jQuery(window).resizeDelayed( false, id );

Удалить все функции

jQuery(window).resizeDelayed( false );

ИСПОЛЬЗОВАНИЕ:

// ADD SOME FUNCTIONS TO RESIZE EVENT
jQuery(window).resizeDelayed( function(){ console.log( 'first event - should run after 0.4 seconds'); }, 400,  'id-first-event' );
jQuery(window).resizeDelayed( function(){ console.log('second event - should run after 1.5 seconds'); }, 1500, 'id-second-event' );
jQuery(window).resizeDelayed( function(){ console.log( 'third event - should run after 3.0 seconds'); }, 3000, 'id-third-event' );

// LETS DELETE THE SECOND ONE
jQuery(window).resizeDelayed( false, 'id-second-event' );

// LETS ADD ONE WITH AUTOGENERATED ID(THIS COULDNT BE DELETED LATER) AND DEFAULT TIMEOUT (500ms)
jQuery(window).resizeDelayed( function(){ console.log('newest event - should run after 0.5 second'); } );

// LETS CALL RESIZE EVENT MANUALLY MULTIPLE TIMES (OR YOU CAN RESIZE YOUR BROWSER WINDOW) TO SEE WHAT WILL HAPPEN
jQuery(window).resize().resize().resize().resize().resize().resize().resize();

ВЫХОД ИСПОЛЬЗОВАНИЯ:

first event - should run after 0.4 seconds
newest event - should run after 0.5 second
third event - should run after 3.0 seconds

PLUGIN:

jQuery.fn.resizeDelayed = (function(){

    // >>> THIS PART RUNS ONLY ONCE - RIGHT NOW

    var rd_funcs = [], rd_counter = 1, foreachResizeFunction = function( func ){ for( var index in rd_funcs ) { func(index); } };

    // REGISTER JQUERY RESIZE EVENT HANDLER
    jQuery(window).resize(function() {

        // SET/RESET TIMEOUT ON EACH REGISTERED FUNCTION
        foreachResizeFunction(function(index){

            // IF THIS FUNCTION IS MANUALLY DISABLED ( by calling jQuery(window).resizeDelayed(false, 'id') ),
            // THEN JUST CONTINUE TO NEXT ONE
            if( rd_funcs[index] === false )
                return; // CONTINUE;

            // IF setTimeout IS ALREADY SET, THAT MEANS THAT WE SHOULD RESET IT BECAUSE ITS CALLED BEFORE DURATION TIME EXPIRES
            if( rd_funcs[index].timeout !== false )
                clearTimeout( rd_funcs[index].timeout );

            // SET NEW TIMEOUT BY RESPECTING DURATION TIME
            rd_funcs[index].timeout = setTimeout( rd_funcs[index].func, rd_funcs[index].delay );

        });

    });

    // <<< THIS PART RUNS ONLY ONCE - RIGHT NOW

    // RETURN THE FUNCTION WHICH JQUERY SHOULD USE WHEN jQuery(window).resizeDelayed(...) IS CALLED
    return function( func_or_false, delay_or_id, id ){

        // FIRST PARAM SHOULD BE SET!
        if( typeof func_or_false == "undefined" ){

            console.log( 'jQuery(window).resizeDelayed(...) REQUIRES AT LEAST 1 PARAMETER!' );
            return this; // RETURN JQUERY OBJECT

        }

        // SHOULD WE DELETE THE EXISTING FUNCTION(S) INSTEAD OF CREATING A NEW ONE?
        if( func_or_false == false ){

            // DELETE ALL REGISTERED FUNCTIONS?
            if( typeof delay_or_id == "undefined" ){

                // CLEAR ALL setTimeout's FIRST
                foreachResizeFunction(function(index){

                    if( typeof rd_funcs[index] != "undefined" && rd_funcs[index].timeout !== false )
                        clearTimeout( rd_funcs[index].timeout );

                });

                rd_funcs = [];

                return this; // RETURN JQUERY OBJECT

            }
            // DELETE ONLY THE FUNCTION WITH SPECIFIC ID?
            else if( typeof rd_funcs[delay_or_id] != "undefined" ){

                // CLEAR setTimeout FIRST
                if( rd_funcs[delay_or_id].timeout !== false )
                    clearTimeout( rd_funcs[delay_or_id].timeout );

                rd_funcs[delay_or_id] = false;

                return this; // RETURN JQUERY OBJECT

            }

        }

        // NOW, FIRST PARAM MUST BE THE FUNCTION
        if( typeof func_or_false != "function" )
            return this; // RETURN JQUERY OBJECT

        // SET THE DEFAULT DELAY TIME IF ITS NOT ALREADY SET
        if( typeof delay_or_id == "undefined" || isNaN(delay_or_id) )
            delay_or_id = 500;

        // SET THE DEFAULT ID IF ITS NOT ALREADY SET
        if( typeof id == "undefined" )
            id = rd_counter;

        // ADD NEW FUNCTION TO RESIZE EVENT
        rd_funcs[id] = {
            func : func_or_false,
            delay: delay_or_id,
            timeout : false
        };

        rd_counter++;

        return this; // RETURN JQUERY OBJECT

    }

})();
Дежан Кёдич
источник
1

Предполагая, что курсор мыши должен вернуться к документу после изменения размера окна, мы можем создать поведение, похожее на обратный вызов, с событием onmouseover. Не забывайте, что это решение может не работать для сенсорных экранов, как ожидалось.

var resizeTimer;
var resized = false;
$(window).resize(function() {
   clearTimeout(resizeTimer);
   resizeTimer = setTimeout(function() {
       if(!resized) {
           resized = true;
           $(document).mouseover(function() {
               resized = false;
               // do something here
               $(this).unbind("mouseover");
           })
       }
    }, 500);
});
Онур
источник
Прекрасно подходит для сайтов, основанных только на мышке, но событие наведения мыши никогда не произойдет, если, например, пользователь находится на мобильном телефоне и переключается с пейзажа на портрет, или если он изменил размер окна на рабочем столе с сенсорным экраном.
user56reinstatemonica8
Вы можете изменить размер окна браузера с помощью клавиатуры в последних версиях Windows, таких как Win-Arrow-Left Win-Arrow-Right и т. Д.
Lu4
0

Это то, что я реализовал:

$ (window) .resize (function () {setTimeout (someFunction, 500);});

мы можем очистить setTimeout, если ожидаем, что изменение размера произойдет меньше, чем500ms

Удачи...

Акаша
источник