JQuery: Como chamar o evento RESIZE apenas depois de seu redimensionamento CONCLUÍDO?

109

Como chamo uma função depois que a janela do navegador TERMINAR o redimensionamento?

Estou tentando fazer assim, mas estou tendo problemas. Estou usando a função de evento JQuery Resize:

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

No entanto, essa função é chamada continuamente se o usuário estiver redimensionando manualmente a janela do navegador. O que significa que ele pode chamar essa função dezenas de vezes em um curto intervalo de tempo.

Como posso chamar a função de redimensionamento apenas uma vez (depois que a janela do navegador terminar de redimensionar)?

ATUALIZAR

Também sem ter que usar uma variável global.

nickb
fonte
@BGerrissen, se você puder me mostrar como fazer jsfiddle.net/Zevan/c9UE5/1 sem uma variável global, eu definitivamente farei :)
nickb
o método cleartimeout / settimeout acima funciona maravilhosamente bem.
kris-o3

Respostas:

129

Aqui está um exemplo usando as instruções do jh

Você pode armazenar um id de referência para qualquer setInterval ou setTimeout. Como isso:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);
Zevan
fonte
Adoro que este código seja tão simples. Alguma maneira de fazer isso funcionar sem ter que usar uma variável global?
nickb
Se eu
descobrir
sem problemas. Existem algumas maneiras. Editei a resposta para refletir a mais simples que consegui pensar.
Zevan
obrigado pela atualização, mas parece que ainda usa uma variável global. Você pode atualizá-lo para não exigir uma variável global (var id). Obrigado
nickb
4
Acho que você perdeu o link no final do post ... confira: jsfiddle.net/Zevan/c9UE5/5
Zevan
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 
} ) );

Observe, você pode usar este método para qualquer coisa que quiser depurar (eventos chave, etc.).

Ajuste o parâmetro de tempo limite para obter o efeito desejado ideal.

BGerrissen
fonte
1
Acabei de tentar executar isso e parece executar DUAS VEZES . Substituí "// fazer coisas" por "$ (" corpo "). Append (" <br/> FEITO! ");" e chama isso de DUAS VEZES em um redimensionamento do navegador
apelido
oops ... Esqueci uma parte bastante importante (na verdade, definir o timeoutID) ... Corrigido agora, tente novamente;)
BGerrissen
3
@ user43493: Ele chama o cuntion func, com o thisponteiro interno apontando para scope, e com Array.prototype.slice.call(args)(o que gera uma matriz padrão de args) como os argumentos
Eric
4
Esta é uma abstração reutilizável, então você não precisa codificar manualmente os tempos limite ou manter o controle dos IDs de tempo limite globais. Você pode usá-lo para muito mais do que apenas redimensionar a janela;) por exemplo, para eliminar um botão de envio passando um parâmetro de tempo limite superior. Você pode optar pela solução de menos código, mas aconselho você a manter este trecho em seu kit, você irá apreciá-lo mais tarde;)
BGerrissen
2
Underscore.js tem uma boa implementação disso se você já estiver usando essa lib. underscorejs.org/#debounce
twmulloy
22

Você pode usar setTimeout()e clearTimeout()em conjunto com jQuery.data:

$(window).resize(function() {
    clearTimeout($.data(this, 'resizeTimer'));
    $.data(this, 'resizeTimer', setTimeout(function() {
        //do something
        alert("Haven't resized in 200ms!");
    }, 200));
});

Atualizar

Eu escrevi uma extensão para aprimorar o manipulador de eventos padrão on(& bind) do jQuery. Ele anexa uma função de manipulador de eventos para um ou mais eventos aos elementos selecionados se o evento não foi disparado para um determinado intervalo. Isso é útil se você deseja disparar um retorno de chamada somente após um atraso, como o evento de redimensionamento ou então. 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));

Use-o como qualquer outro gerenciador de evento onou bind, exceto que você pode passar um parâmetro extra como último:

$(window).on('resize', function(e) {
    console.log(e.type + '-event was 200ms not triggered');
}, 200);

http://jsfiddle.net/ARTsinn/EqqHx/

Yckart
fonte
1
Literalmente, faça a mesma pesquisa no Google todos os dias. e vá para a mesma página para este pedaço de código. Queria apenas lembrar disso haha.
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
fonte
Eu gosto mais disso, mas por que não usar chaves em suas declarações condicionais? Eu sei que funciona sem eles, mas é uma dor para outros desenvolvedores olharem;)
teewuane
7

Apenas para adicionar ao acima, é comum obter eventos de redimensionamento indesejados por causa das barras de rolagem que aparecem e saem. Aqui estão alguns códigos para evitar isso:

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

veja jsfiddle

kofifus
fonte
5

Underscore.js tem alguns métodos excelentes para essa tarefa: throttlee debounce. Mesmo se você não estiver usando o Underscore, dê uma olhada na fonte dessas funções. Aqui está um exemplo:

var redraw = function() {'redraw logic here'};
var debouncedRedraw = _.debounce(redraw, 750);
$(window).on('resize', debouncedRedraw);
tybro0103
fonte
2

Esta é a minha abordagem:

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

O resize-event-listener captura todas as chamadas de redimensionamento de entrada, cria uma função de tempo limite para cada uma e salva o identificador de tempo limite junto com um número de iteração prefixado por 'id-'(para ser usado como chave de array) no tos-array.

cada vez que o tempo limite é disparado, ele chama a fnfunção -funcional que verifica se esse foi o último tempo limite na tosmatriz (a função fn exclui todos os tempos limite executados). se verdadeiro (= if(len-1 == 0)), o redimensionamento está concluído.

lsblsb
fonte
Seria bom se você tivesse alguns comentários ou uma explicação sobre o que está fazendo aqui
Brett Gregson
1

jQuery fornece um offmétodo para remover o manipulador de eventos

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
Qiang
fonte