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.


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

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.


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/


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

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


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

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.


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);
    }
});
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.