JavaScript / JQuery: $ (window). Redimensionar como disparar APÓS o redimensionamento ser concluído?

235

Estou usando o JQuery como tal:

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

No entanto, parece que, se a pessoa redimensionar manualmente as janelas do navegador, arrastando a borda da janela para aumentá-la / diminuir, o .resizeevento acima será acionado várias vezes.

Pergunta: Como chamo uma função APÓS o redimensionamento da janela do navegador concluído (para que o evento seja acionado apenas uma vez)?

sarah
fonte
Veja esta resposta: stackoverflow.com/questions/667426/… Envolve o uso de tempos limite para atrasar a execução de sua função.
Alastair Pitts
Eu não sei se isso é possível, você pode tentar este plugin embora benalman.com/projects/jquery-resize-plugin
BrunoLM
A propósito, notei que isso é muito útil em navegadores móveis que tendem a ocultar gradualmente a barra de endereços quando o usuário rola para baixo, redimensionando a tela. Eu esperava também redimensionamento do ecrã para acontecer somente no ambiente de trabalho ...
MakisH

Respostas:

299

Aqui está uma modificação da solução do CMS que pode ser chamada em vários locais no seu código:

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

Uso:

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

A solução do CMS é boa se você a chamar apenas uma vez, mas se a chamar várias vezes, por exemplo, se partes diferentes do seu código configurarem retornos de chamada separados para redimensionar janelas, haverá falha porque eles compartilham a timervariável.

Com essa modificação, você fornece um ID exclusivo para cada retorno de chamada, e esses IDs exclusivos são usados ​​para manter todos os eventos de tempo limite separados.

Brahn
fonte
2
Eu <3 u, eu combinei isso enquanto redimensionava gráficos Highcharts em tela cheia (não html5) e funciona muito bem.
Michael J. Calkins
Absolutamente incrível!
Starkers
2
Como eu me beneficiei tanto com sua resposta, pensei em compartilhar um protótipo funcional do seu código fornecido para o resto da comunidade: jsfiddle.net/h6h2pzpu/3 . Aprecie todos!
Jonas Stawski 27/08/15
1
Tudo isso é perfeito, mas atrasa a execução da função real pela quantidade de milissegundos (ms), que pode ser MUITO INDESEJÁVEL.
Tomas M
Isso simplesmente perfeito: existe uma maneira de obter o último redimensionamento e não precisar retransmitir esses milissegundos? De qualquer forma, isso acabou de salvar o meu dia: ') Obrigado.
Leo
138

Eu prefiro criar um evento:

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

Aqui está como você o cria:

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

Você pode ter isso em um arquivo javascript global em algum lugar.

Carlos Martinez T
fonte
Eu usei esta solução. Mas, a longo prazo, gostaria de implementar o mesmo que a solução de brahn.
Bharat Patil
isso permite que você vincular a esse evento em todo o lugar, não apenas em uma função, supondo que você tem várias páginas / .js que exigem reações separadas para isso é claro
hanzolo
Isto não funcionou para mim, mas DusanV resposta fez o trabalho
lightbyte
123

Eu uso a seguinte função para atrasar ações repetidas, ele funcionará para o seu caso:

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

Uso:

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

A função de retorno de chamada transmitida a ele será executada somente quando a última chamada para atrasar for feita após o período especificado, caso contrário, um timer será redefinido. Acho isso útil para outros fins, como detectar quando o usuário parou de digitar etc. ..

CMS
fonte
5
Eu acho que essa abordagem pode falhar se usada várias vezes (por exemplo, se partes diferentes do retorno de chamada de configuração de código $(window).resize) forem porque todos eles compartilharão a timervariável. Veja minha resposta abaixo para a solução proposta.
Brahn
Muito agradável! Funciona como um encanto! Como suas respostas ... simples e elegante!
Sr. Pumpkin
74

Se você tiver o Underscore.js instalado, poderá:

$(window).resize(_.debounce(function(){
    alert("Resized");
},500));
JT.
fonte
1
Mesmo se você não quiser incluir sublinhado pelo menos agarrar a fonte para isso: underscorejs.org/docs/underscore.html#section-67
tybro0103
Debounce é a técnica correta aqui, é apenas uma questão de implementações. E essa é a implementação superior se o Underscore / Lodash já for uma dependência do projeto.
Fatore Mystic
Agora é isso que eu estou usando,
Bharat Patil
20

Algumas das soluções mencionadas anteriormente não funcionaram para mim, embora sejam de uso mais geral. Como alternativa, eu encontrei este que fez o trabalho no redimensionamento da janela:

$(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
fonte
1
Apenas o seu exemplo funcionou para mim ... os outros acima não! Não sei por que sua resposta não foi selecionada.
Mumbo Jumbo
Eu não entendo por que para pegar o evento de redimensionamento dentro do evento de redimensionamento, mas funciona para mim também ...
lightbyte
Mumbo Jumbo, acho que porque outros se concentram na generalização, procurando solução para qualquer problema desse tipo (várias chamadas de uma função).
DusanV
lightbyte, é porque você precisa interromper os eventos anteriores cada vez que a função é chamada.
DusanV
13

Muito obrigado a David Walsh, aqui está uma versão básica do debounce de sublinhado.

Código:

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

Uso simples:

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

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

Ref: http://davidwalsh.name/javascript-debounce-function

Irvin Dominin
fonte
6

Na verdade, como eu sei, você não pode executar algumas ações exatamente quando o redimensionamento está desativado, simplesmente porque você não conhece as ações do usuário futuro. Mas você pode assumir o tempo decorrido entre dois eventos de redimensionamento; portanto, se você esperar um pouco mais que esse tempo e nenhum redimensionamento for feito, poderá chamar sua função.
A idéia é que usamos setTimeoute seu ID para salvá-lo ou excluí-lo. Por exemplo, sabemos que o tempo entre dois eventos de redimensionamento é de 500 ms; portanto, esperaremos 750 ms.

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

newint
fonte
3

Declarar ouvinte com atraso global:

var resize_timeout;

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

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

E abaixo, use os ouvintes para o resizedevento como desejar:

$(window).on('resized', function(){
    console.log('resized');
});
gzzz
fonte
Obrigado, essa é a melhor solução atualmente. Também testei que 250ms está funcionando da melhor maneira ao redimensionar a janela.
AlexioVay
2

Plugin jQuery simples para evento de redimensionamento de janela atrasado.

SINTAXE:

Adicionar nova função para redimensionar evento

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

Remova a função (declarando seu ID) adicionada anteriormente

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

Remova todas as funções

jQuery(window).resizeDelayed( false );

USO:

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

SAÍDA DE USO:

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

PLUGAR:

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

    }

})();
Déján Kőŕdić
fonte
1

Supondo que o cursor do mouse retorne ao documento após o redimensionamento da janela, podemos criar um comportamento de retorno de chamada com o evento onmouseover. Não esqueça que esta solução pode não funcionar em telas ativadas para toque conforme o esperado.

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);
});
onur
fonte
Ótimo para sites somente para desktop baseados em mouse, mas o evento de mouseover nunca acontecerá se, por exemplo, o usuário estiver em um celular e mudar de paisagem para retrato ou se redimensionar a janela em uma área de trabalho com tela de toque.
precisa saber é o seguinte
Você pode redimensionar a janela do navegador com a ajuda de teclado na últimas oses Windows como Win-Seta-Esquerda Win-seta para a direita, etc ...
LU4
0

Isto é o que eu implementei:

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

podemos limpar o setTimeout se esperamos que o redimensionamento ocorra menos que500ms

Boa sorte...

Akash
fonte