JQuery: как вызвать событие RESIZE только после завершения изменения размера?

109

Как вызвать функцию после ЗАВЕРШЕНИЯ изменения размера окон браузера?

Я пытаюсь так сделать, но возникают проблемы. Я использую функцию события JQuery Resize:

$(window).resize(function() {
  ... // how to call only once the browser has FINISHED resizing?
});

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

Как я могу вызвать функцию изменения размера только один раз (после завершения изменения размера окна браузера)?

ОБНОВИТЬ

Также без использования глобальной переменной.

nickb
источник
@BGerrissen, если вы можете показать мне, как сделать jsfiddle.net/Zevan/c9UE5/1 без глобальной переменной, я обязательно это сделаю :)
nickb
Вышеупомянутый метод cleartimeout / settimeout отлично работает.
kris-o3

Ответы:

129

Вот пример с использованием инструкций jh

Вы можете сохранить ссылочный идентификатор для любого setInterval или setTimeout. Как это:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);
Зеван
источник
Мне нравится, что этот код такой простой. Есть ли способ заставить эту работу работать без использования глобальной переменной?
nickb
Если я увижу, как это сделать без глобальной переменной, я сделаю это «принятым ответом»
никб
без проблем. Есть несколько способов. Я отредактировал ответ, чтобы отразить самый простой из возможных.
Zevan
спасибо за обновление, но похоже, что он все еще использует глобальную переменную. Можете ли вы обновить его, чтобы не требовать глобальную переменную (var id). Спасибо
nickb
4
Я думаю, вы пропустили ссылку в конце поста ... зацените
Зеван
85

Debounce .

function debouncer( func , timeout ) {
   var timeoutID , timeout = timeout || 200;
   return function () {
      var scope = this , args = arguments;
      clearTimeout( timeoutID );
      timeoutID = setTimeout( function () {
          func.apply( scope , Array.prototype.slice.call( args ) );
      } , timeout );
   }
}


$( window ).resize( debouncer( function ( e ) {
    // do stuff 
} ) );

Обратите внимание: вы можете использовать этот метод для всего, что хотите отменить (ключевые события и т. Д.).

Настройте параметр тайм-аута для получения оптимального желаемого эффекта.

BGerrissen
источник
1
Я просто попробовал запустить это, и, похоже, он запускается ДВАЖДЫ . Я заменил "// делать что-то" на "$ (" body "). Append (" <br/> DONE! ");" и он называет это ДВАЖДЫ при изменении размера браузера
nickb
упс ... Я забыл довольно важную часть (на самом деле установка timeoutID) ... Исправлено, попробуйте еще раз;)
BGerrissen
3
@ user43493: Он вызывает cuntion funcс внутренним thisуказателем, указывающим на scope, и с Array.prototype.slice.call(args)(который генерирует стандартный массив из args) в качестве аргументов
Эрик
4
Это многократно используемая абстракция, поэтому вам не нужно вручную кодировать тайм-ауты или самостоятельно отслеживать глобальные timeoutID. Вы можете использовать его не только для изменения размера окна;) например, чтобы заблокировать кнопку отправки, передав параметр с более высоким таймаутом. Вы можете выбрать решение с меньшим количеством кода, но я советую вам сохранить этот фрагмент в своем наборе, вы оцените его позже;)
BGerrissen
2
В Underscore.js есть хорошая реализация, если вы уже используете эту библиотеку. underscorejs.org/#debounce
twmulloy
22

Вы можете использовать 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) 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/

yckart
источник
1
Буквально каждый день выполняйте один и тот же поиск в Google. и перейдите на ту же страницу для этого фрагмента кода. Хотел бы я просто вспомнить это, ха-ха.
Dustin Silk
7
var lightbox_resize = false;
$(window).resize(function() {
    console.log(true);
    if (lightbox_resize)
        clearTimeout(lightbox_resize);
    lightbox_resize = setTimeout(function() {
        console.log('resize');
    }, 500);
});
Jlis
источник
Мне это нравится больше всего, но почему бы не использовать фигурные скобки в условных выражениях? Я знаю, что это работает и без них, но другим разработчикам
сложно
7

Чтобы добавить к вышесказанному, часто возникают нежелательные события изменения размера из-за того, что полосы прокрутки появляются и исчезают, вот код, чтобы избежать этого:

function registerResize(f) {
    $(window).resize(function() {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = setTimeout(function() {
            var oldOverflow = document.body.style.overflow;
            document.body.style.overflow = "hidden";
            var currHeight = $(window).height(),
                currWidth = $(window).width();
            document.body.style.overflow = oldOverflow;

            var prevUndefined = (typeof this.prevHeight === 'undefined' || typeof this.prevWidth === 'undefined');
            if (prevUndefined || this.prevHeight !== currHeight || this.prevWidth !== currWidth) {
                //console.log('Window size ' + (prevUndefined ? '' : this.prevHeight + "," + this.prevWidth) + " -> " + currHeight + "," + currWidth);
                this.prevHeight = currHeight;
                this.prevWidth = currWidth;

                f(currHeight, currWidth);
            }
        }, 200);
    });
    $(window).resize(); // initialize
}

registerResize(function(height, width) {
    // this will be called only once per resize regardless of scrollbars changes
});

см. jsfiddle

kofifus
источник
5

В Underscore.js есть несколько отличных методов для решения этой задачи: throttleи debounce. Даже если вы не используете Underscore, взгляните на источник этих функций. Вот пример:

var redraw = function() {'redraw logic here'};
var debouncedRedraw = _.debounce(redraw, 750);
$(window).on('resize', debouncedRedraw);
tybro0103
источник
2

Это мой подход:

document.addEventListener('DOMContentLoaded', function(){
    var tos = {};
    var idi = 0;
    var fn  = function(id)
    {
        var len = Object.keys(tos).length;

        if(len == 0)
            return;

        to = tos[id];
        delete tos[id];

        if(len-1 == 0)
            console.log('Resize finished trigger');
    };

    window.addEventListener('resize', function(){
        idi++;
        var id = 'id-'+idi;
        tos[id] = window.setTimeout(function(){fn(id)}, 500);
    });
});

Resize-event-listener улавливает все входящие вызовы изменения размера, создает функцию тайм-аута для каждого и сохраняет идентификатор тайм-аута вместе с номером итерации, добавленным 'id-'(для использования в качестве ключа массива) в tos-array.

Каждый раз, когда срабатывает тайм-аут, он вызывает fn-функцию, которая проверяет, был ли это последний тайм-аут в tosмассиве (функция fn удаляет каждый выполненный тайм-аут). если true (= if(len-1 == 0)), изменение размера завершено.

lsblsb
источник
Было бы неплохо, если бы у вас были некоторые комментарии или объяснения относительно того, что вы здесь делаете,
Бретт Грегсон
1

jQuery предоставляет offметод для удаления обработчика событий

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
Цян
источник