Como aguardar o evento 'end' do 'redimensionar' e somente então executar uma ação?

243

Atualmente, uso algo como:

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

Mas isso é chamado muitas vezes enquanto o processo de redimensionamento continua. É possível capturar um evento quando ele termina?

Rella
fonte
Talvez anexar usando .one()para que ele só seja executado depois que todo o redimensionamento for feito e não repetidamente?
Brad Christie
5
Quando um usuário redimensiona uma janela manualmente (arrastando-a), o evento redimensionar será chamado mais de uma vez; portanto, usar .one () realmente não será eficaz.
Jessegavin
O uso de uma função anônima no exemplo acima pode ser removido, por simplicidade e rapidez marginal: $ (janela) .Resize (resizedw)
Fornost
Aqui está uma biblioteca jQuery para isso: github.com/nielse63/jquery.resizeend
rugk:

Respostas:

177

Tive sorte com a seguinte recomendação: http://forum.jquery.com/topic/the-resizeend-event

Aqui está o código para que você não precise procurar no link e na fonte do post:

var rtime;
var timeout = false;
var delta = 200;
$(window).resize(function() {
    rtime = new Date();
    if (timeout === false) {
        timeout = true;
        setTimeout(resizeend, delta);
    }
});

function resizeend() {
    if (new Date() - rtime < delta) {
        setTimeout(resizeend, delta);
    } else {
        timeout = false;
        alert('Done resizing');
    }               
}

Obrigado sime.vidas pelo código!

Dolan Antenucci
fonte
1
Alguém pode querer mudar a Data para algo como new Date(-1E12)- isto é, JSLint adverte sobre o uso 00.
elundmark
Obrigado elundmark. Eu mudei a instanciação de data para usar zeros simples; espero que isso não gere uma reclamação.
Dolan Antenucci
@elundmark ou use + operação. rtime: Date; .... if (+new Date() - +rtime < delta)e na função de redimensionar texto datilografado deve ser uma função de seta como esta resizeend=()=>. Como na função redimensionar, thisfaça referência ao objeto da janela.
Muhammet pode TONBUL
517

Você pode usar setTimeout()eclearTimeout()

function resizedw(){
    // Haven't resized in 100ms!
}

var doit;
window.onresize = function(){
  clearTimeout(doit);
  doit = setTimeout(resizedw, 100);
};

Exemplo de código no jsfiddle .

Mark Coleman
fonte
7
Esta é uma ótima resposta. Ele faz o que o plugin que eu recomendo faz, apenas sem um plugin.
Jessegavin
Eu acho que a única maneira de realmente melhorar isso é detectar o movimento do mouse. Eu suspeito que cavar nele não seria recompensa, no entanto.
Michael Haren
Isso funciona apenas se o redimensionamento for concluído em um segundo? Minha função foi desencadeando quando eu tentei usar isso (eu era lento w / minha janela redimensionamento embora)
Dolan Antenucci
@MichaelHaren Como a alça re-size é geralmente fora do $(document), de detecção do rato seria limitada a usuários que executam o Microsoft Windows e versões vulneráveis do seu Internet Explorer: iedataleak.spider.io/demo
Alastair
12
Esta é uma implementação muito simples do conceito de debounce ( unscriptable.com/2009/03/20/debouncing-javascript-methods ). Paul Irish (e outros) apresentou uma solução muito mais eficiente que não está lidando com eventos de redimensionamento "desnecessários": paulirish.com/2009/throttled-smartresize-jquery-event-handler
rmoestl
78

Este é o código que eu escrevo de acordo com a resposta de @Mark Coleman:

$(window).resize(function() {
    clearTimeout(window.resizedFinished);
    window.resizedFinished = setTimeout(function(){
        console.log('Resized finished.');
    }, 250);
});

Obrigado Mark!

Roy Shoa
fonte
1
Nice approarch. Também mencionado aqui com a diferença de que nenhuma modificação na janela da super variável é feita.
Alwin Kesler
2
@AlwinKesler - no seu exemplo, a variável resizeTimeré uma variável global, o que significa que não é definida window, portanto é exatamente a mesma que aqui, apenas este exemplo é melhor, pois você não precisa definir a variável externamente. e também faz sentido adicionar essa variável ao windowobjeto, pois esse é o objeto ao qual o ouvinte de evento está vinculado.
vsync 21/06
1
Obrigado! Só queria acrescentar que, em alguns casos, é necessário um intervalo de tempo mais longo para executar determinadas tarefas em um retorno de chamada. Por exemplo, no meu caso, 250 não funcionaram, mas 700 funcionaram muito bem.
Maria Blair
Melhor solução.
Daniel Dewhurst
36

O Internet Explorer fornece um evento resizeEnd . Outros navegadores acionarão o evento de redimensionamento várias vezes enquanto você estiver redimensionando.

Existem outras ótimas respostas aqui que mostram como usar setTimeout e o .throttle ,.debounce métodos de lodash e sublinhado, então vou mencionar o plugin jQuery do acelerador-debounce de Ben Alman, que realiza o que você procura .

Suponha que você tenha esta função que deseja acionar após um redimensionamento:

function onResize() {
  console.log("Resize just happened!");
};

Exemplo de aceleração
No exemplo a seguir, onResize()será chamado apenas uma vez a cada 250 milissegundos durante o redimensionamento da janela.

$(window).resize( $.throttle( 250, onResize) );

Exemplo de rejeição
No exemplo a seguir, onResize()será chamado apenas uma vez no final de uma ação de redimensionamento de janela. Isso alcança o mesmo resultado que o @Mark apresenta em sua resposta.

$(window).resize( $.debounce( 250, onResize) );
jessegavin
fonte
1
O Lodash também é útil aqui, que também possui os métodos _.throttle e _.debounce. Eu acho que o rebounce é uma abordagem superior em comparação com o exemplo aceito acima.
Kevin Leary
1
Sim, esta resposta foi escrita há mais de 5 anos. Muita coisa aconteceu desde os dias do plugin jQuery. Aqui está uma função autônoma debounce também davidwalsh.name/javascript-debounce-function
jessegavin
10

Uma solução é estender o jQuery com uma função, por exemplo: resized

$.fn.resized = function (callback, timeout) {
    $(this).resize(function () {
        var $this = $(this);
        if ($this.data('resizeTimeout')) {
            clearTimeout($this.data('resizeTimeout'));
        }
        $this.data('resizeTimeout', setTimeout(callback, timeout));
    });
};

Uso da amostra:

$(window).resized(myHandler, 300);

Jone Polvora
fonte
7

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

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);

Para fazer isso sem uma variável "global", você pode adicionar uma variável local à própria função. Ex:

$(window).resize(function() {
    clearTimeout(this.id);
    this.id = setTimeout(doneResizing, 500);
});

function doneResizing(){
  $("body").append("<br/>done!");   
}
Xman Classical
fonte
4

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 padrão on(& bind) -event 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 acionado por um determinado intervalo. Isso é útil se você deseja acionar 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 manipulador onou bind-event, 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
3

Existe um método muito mais simples de executar uma função no final do redimensionamento do que calcular o tempo delta entre duas chamadas, basta fazer o seguinte:

var resizeId;
$(window).resize(function() {
    clearTimeout(resizeId);
    resizeId = setTimeout(resizedEnded, 500);
});

function resizedEnded(){
    ...
}

E o equivalente para Angular2 :

private resizeId;
@HostListener('window:resize', ['$event'])
onResized(event: Event) {
  clearTimeout(this.resizeId);
  this.resizeId = setTimeout(() => {
    // Your callback method here.
  }, 500);
}

Para o método angular, use a () => { }notação em setTimeoutpara preservar o escopo; caso contrário, você não poderá fazer nenhuma chamada ou função de função this.

Antoine Thiry
fonte
2

Esta é uma modificação do código de Dolan acima. Adicionei um recurso que verifica o tamanho da janela no início do redimensionamento e o compara ao tamanho no final do redimensionamento, se o tamanho for maior ou menor que a margem ( por exemplo, 1000) e depois recarrega.

var rtime = new Date(1, 1, 2000, 12,00,00);
var timeout = false;
var delta = 200;
var windowsize = $window.width();
var windowsizeInitial = $window.width();

$(window).on('resize',function() {
    windowsize = $window.width();
    rtime = new Date();
    if (timeout === false) {
            timeout = true;
            setTimeout(resizeend, delta);
        }
});

function resizeend() {
if (new Date() - rtime < delta) {
    setTimeout(resizeend, delta);
    return false;
} else {
        if (windowsizeInitial > 1000 && windowsize > 1000 ) {
            setTimeout(resizeend, delta);
            return false;
        }
        if (windowsizeInitial < 1001 && windowsize < 1001 ) {
            setTimeout(resizeend, delta);
            return false;
        } else {
            timeout = false;
            location.reload();
        }
    }
    windowsizeInitial = $window.width();
    return false;
}
Sami A.
fonte
2

A resposta de Mark Coleman é certamente muito melhor que a resposta selecionada, mas se você deseja evitar a variável global para o ID de tempo limite (a doitvariável na resposta de Mark), você pode fazer um dos seguintes:

(1) Use uma expressão de função chamada imediatamente (IIFE) para criar um fechamento.

$(window).resize((function() { // This function is immediately invoked
                               // and returns the closure function.
    var timeoutId;
    return function() {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() {
            timeoutId = null; // You could leave this line out.
            // Code to execute on resize goes here.
        }, 100);
    };
})());

(2) Use uma propriedade da função de manipulador de eventos.

$(window).resize(function() {
    var thisFunction = arguments.callee;
    clearTimeout(thisFunction.timeoutId);
    thisFunction.timeoutId = setTimeout(function() {
        thisFunction.timeoutId = null; // You could leave this line out.
        // Code to execute on resize goes here.
    }, 100);
});
John S
fonte
A opção 2, usando argument.callee, não funcionará se a função for transpilada do ES6.
Martin Burch
1

eu escrevi uma função wrapper litte sozinho ...

onResize  =   function(fn) {
    if(!fn || typeof fn != 'function')
        return 0;

    var args    = Array.prototype.slice.call(arguments, 1);

    onResize.fnArr    = onResize.fnArr || [];
    onResize.fnArr.push([fn, args]);

    onResize.loop   = function() {
        $.each(onResize.fnArr, function(index, fnWithArgs) {
            fnWithArgs[0].apply(undefined, fnWithArgs[1]);
        });
    };

    $(window).on('resize', function(e) {
        window.clearTimeout(onResize.timeout);
        onResize.timeout    = window.setTimeout("onResize.loop();", 300);
    });
};

Aqui está o uso:

var testFn  = function(arg1, arg2) {
    console.log('[testFn] arg1: '+arg1);
    console.log('[testFn] arg2: '+arg2);
};

// document ready
$(function() {
    onResize(testFn, 'argument1', 'argument2');
});
user2123506
fonte
1
(function(){
    var special = jQuery.event.special,
        uid1 = 'D' + (+new Date()),
        uid2 = 'D' + (+new Date() + 1);

    special.resizestart = {
        setup: function() {
            var timer,
                handler =  function(evt) {
                    var _self = this,
                        _args = arguments;
                    if (timer) {
                        clearTimeout(timer);
                    } else {
                        evt.type = 'resizestart';
                        jQuery.event.handle.apply(_self, _args);
                    }

                    timer = setTimeout( function(){
                        timer = null;
                    }, special.resizestop.latency);
                };
            jQuery(this).bind('resize', handler).data(uid1, handler);
        },
        teardown: function(){
            jQuery(this).unbind( 'resize', jQuery(this).data(uid1) );
        }
    };

    special.resizestop = {
        latency: 200,
        setup: function() {
            var timer,
                handler = function(evt) {
                    var _self = this,
                        _args = arguments;
                    if (timer) {
                        clearTimeout(timer);
                    }
                    timer = setTimeout( function(){
                        timer = null;
                        evt.type = 'resizestop';
                        jQuery.event.handle.apply(_self, _args);
                    }, special.resizestop.latency);
                };

            jQuery(this).bind('resize', handler).data(uid2, handler);
        },
        teardown: function() {
            jQuery(this).unbind( 'resize', jQuery(this).data(uid2) );
        }
    };
})();

$(window).bind('resizestop',function(){
    //...
});
lazer
fonte
1

Bem, no que diz respeito ao gerenciador de janelas, cada evento de redimensionamento é sua própria mensagem, com um começo e um fim distintos; portanto, tecnicamente, toda vez que a janela é redimensionada, é o fim.

Dito isto, talvez você queira atrasar sua continuação? Aqui está um exemplo.

var t = -1;
function doResize()
{
    document.write('resize');
}
$(document).ready(function(){
    $(window).resize(function(){
        clearTimeout(t);
        t = setTimeout(doResize, 1000);
    });
});
Demian Brecht
fonte
1

Aqui está um script MUITO simples para acionar os eventos 'redimensionar' e 'redimensionar' no objeto da janela.

Não há necessidade de mexer com datas e horários.

A dvariável representa o número de milissegundos entre os eventos de redimensionamento antes de disparar o evento final de redimensionamento. Você pode brincar com isso para alterar a sensibilidade do evento final.

Para ouvir esses eventos, tudo o que você precisa fazer é:

resizestart: $(window).on('resizestart', function(event){console.log('Resize Start!');});

redimensionar: $(window).on('resizeend', function(event){console.log('Resize End!');});

(function ($) {
    var d = 250, t = null, e = null, h, r = false;

    h = function () {
        r = false;
        $(window).trigger('resizeend', e);
    };

    $(window).on('resize', function (event) {
        e = event || e;
        clearTimeout(t);

        if (!r) {
            $(window).trigger('resizestart', e);
            r = true;
        }

        t = setTimeout(h, d);
    });
}(jQuery));
Neaox
fonte
1
Eu precisava do início e do fim do redimensionamento, e parece que funciona bem (testado no Chrome, FF, Opera e IE11). Para testar, criei um JSFiddle com sua solução: jsfiddle.net/8fsn2joj
Keith DC
1

Isto é o que eu uso para atrasar ações repetidas. Ele pode ser chamado em vários locais no seu código:

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:

$(window).resize(function () { 
   debounce(function() {
          //...
    }, 500);
});
Hil
fonte
0

já que a resposta selecionada não funcionou .. e se você não estiver usando o jquery, aqui está uma função simples do acelerador, com um exemplo de como usá-lo com redimensionamento de janelas

    function throttle(end,delta) {

    var base = this;

    base.wait = false;
    base.delta = 200;
    base.end = end;

    base.trigger = function(context) {

        //only allow if we aren't waiting for another event
        if ( !base.wait ) {

            //signal we already have a resize event
            base.wait = true;

            //if we are trying to resize and we 
            setTimeout(function() {

                //call the end function
                if(base.end) base.end.call(context);

                //reset the resize trigger
                base.wait = false;
            }, base.delta);
        }
    }
};

var windowResize = new throttle(function() {console.log('throttle resize');},200);

window.onresize = function(event) {
    windowResize.trigger();
}
Chad Brown
fonte
0

isso funcionou para mim, pois eu não queria usar nenhum plug-in.

$(window).resize(function() {
    var originalWindowSize = 0;
    var currentWidth = 0;

    var setFn = function () {
        originalWindowSize = $(window).width();
    };

    var checkFn = function () {
        setTimeout(function () {
            currentWidth = $(window).width();
            if (currentWidth === originalWindowSize) {
                console.info("same? = yes") 
                // execute code 
            } else {
                console.info("same? = no"); 
                // do nothing 
            }
        }, 500)
    };
    setFn();
    checkFn();
});

Na redimensionamento da janela, chame "setFn", que obtém a largura da janela e salve como "originalWindowSize". Em seguida, chame "checkFn", que após 500 ms (ou sua preferência) obtém o tamanho atual da janela e compara o original com o atual, se não forem iguais, a janela ainda será redimensionada. Não se esqueça de remover as mensagens do console em produção e (opcional) pode tornar o "setFn" auto-executado.

Byron Wong
fonte
0
var resizeTimer;
$( window ).resize(function() {
    if(resizeTimer){
        clearTimeout(resizeTimer);
    }
    resizeTimer = setTimeout(function() {
        //your code here
        resizeTimer = null;
        }, 200);
    });

Isso funcionou para o que eu estava tentando fazer no chrome. Isso não acionará o retorno de chamada até 200 ms após o último evento de redimensionamento.

frost287
fonte
0

ATUALIZAR!

Melhor alternativa também criada por mim está aqui: https://stackoverflow.com/a/23692008/2829600 (suporta "excluir funções")

POSTO ORIGINAL:

Eu escrevi essa função simples para lidar com atraso na execução, útil dentro de jQuery .scroll () e .resize ().

function delay_exec( id, wait_time, callback_f ){

    // IF WAIT TIME IS NOT ENTERED IN FUNCTION CALL,
    // SET IT TO DEFAULT VALUE: 0.5 SECOND
    if( typeof wait_time === "undefined" )
        wait_time = 500;

    // CREATE GLOBAL ARRAY(IF ITS NOT ALREADY CREATED)
    // WHERE WE STORE CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID
    if( typeof window['delay_exec'] === "undefined" )
        window['delay_exec'] = [];

    // RESET CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID,
    // SO IN THAT WAY WE ARE SURE THAT callback_f WILL RUN ONLY ONE TIME
    // ( ON LATEST CALL ON delay_exec FUNCTION WITH SAME ID  )
    if( typeof window['delay_exec'][id] !== "undefined" )
        clearTimeout( window['delay_exec'][id] );

    // SET NEW TIMEOUT AND EXECUTE callback_f WHEN wait_time EXPIRES,
    // BUT ONLY IF THERE ISNT ANY MORE FUTURE CALLS ( IN wait_time PERIOD )
    // TO delay_exec FUNCTION WITH SAME ID AS CURRENT ONE
    window['delay_exec'][id] = setTimeout( callback_f , wait_time );
}


// USAGE

jQuery(window).resize(function() {

    delay_exec('test1', 1000, function(){
        console.log('1st call to delay "test1" successfully executed!');
    });

    delay_exec('test1', 1000, function(){
        console.log('2nd call to delay "test1" successfully executed!');
    });

    delay_exec('test1', 1000, function(){
        console.log('3rd call to delay "test1" successfully executed!');
    });

    delay_exec('test2', 1000, function(){
        console.log('1st call to delay "test2" successfully executed!');
    });

    delay_exec('test3', 1000, function(){
        console.log('1st call to delay "test3" successfully executed!');
    });

});

/* RESULT
3rd call to delay "test1" successfully executed!
1st call to delay "test2" successfully executed!
1st call to delay "test3" successfully executed!
*/
Déján Kőŕdić
fonte
Você poderia esclarecer o uso aqui? Você está sugerindo que alguém faça $(window).resize(function() { delay_exec('test1', 30, function() { ... delayed stuff here ... }); });:? Código bastante limpo caso contrário. Obrigado por compartilhar. :)
mhulse
Você é demais! Obrigado @ Déján! +1 até o fim. Exemplo de código legal, funciona muito bem com o que eu testei. Simples de usar também. Obrigado novamente por compartilhar. :)
mhulse
0

Eventos ResizeStart e ResizeEnd para janela

http://jsfiddle.net/04fLy8t4/

Eu implementei a função que dispara dois eventos no elemento DOM do usuário:

  1. resizestart
  2. redimensionar

Código:

var resizeEventsTrigger = (function () {
    function triggerResizeStart($el) {
        $el.trigger('resizestart');
        isStart = !isStart;
    }

    function triggerResizeEnd($el) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function () {
            $el.trigger('resizeend');
            isStart = !isStart;
        }, delay);
    }

    var isStart = true;
    var delay = 200;
    var timeoutId;

    return function ($el) {
        isStart ? triggerResizeStart($el) : triggerResizeEnd($el);
    };

})();

$("#my").on('resizestart', function () {
    console.log('resize start');
});
$("#my").on('resizeend', function () {
    console.log('resize end');
});

window.onresize = function () {
    resizeEventsTrigger( $("#my") );
};
WebBrother
fonte
0
var flag=true;
var timeloop;

$(window).resize(function(){
    rtime=new Date();
    if(flag){
        flag=false;
        timeloop=setInterval(function(){
            if(new Date()-rtime>100)
                myAction();
        },100);
    }
})
function myAction(){
    clearInterval(timeloop);
    flag=true;
    //any other code...
}
babak iran
fonte
0

Não sei se meu código funciona para outros, mas é realmente um ótimo trabalho para mim. Tive essa ideia analisando o código Dolan Antenucci, porque a versão dele não funciona para mim e eu realmente espero que seja útil para alguém.

var tranStatus = false;
$(window).resizeend(200, function(){
    $(".cat-name, .category").removeAttr("style");
    //clearTimeout(homeResize);
    $("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) {
      tranStatus = true;
    });
    processResize();
});

function processResize(){
  homeResize = setInterval(function(){
    if(tranStatus===false){
        console.log("not yet");
        $("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) {
            tranStatus = true;
        }); 
    }else{
        text_height();
        clearInterval(homeResize);
    }
  },200);
}
Sai Pone Tha Aung
fonte
0

Eu escrevi uma função que passa uma função quando envolvida em qualquer evento de redimensionamento. Ele usa um intervalo para que o redimensionamento não crie constantemente eventos de tempo limite. Isso permite que ele execute independentemente do evento de redimensionamento, exceto uma entrada de log que deve ser removida na produção.

https://github.com/UniWrighte/resizeOnEnd/blob/master/resizeOnEnd.js

        $(window).resize(function(){
            //call to resizeEnd function to execute function on resize end.
    //can be passed as function name or anonymous function
            resizeEnd(function(){



    });

        });

        //global variables for reference outside of interval
        var interval = null;
        var width = $(window).width();
    var numi = 0; //can be removed in production
        function resizeEnd(functionCall){
            //check for null interval
            if(!interval){
                //set to new interval
                interval = setInterval(function(){
        //get width to compare
                    width2 = $(window).width();
        //if stored width equals new width
                    if(width === width2){
                        //clear interval, set to null, and call passed function
                        clearInterval(interval);
                        interval = null; //precaution
                        functionCall();

                    }
        //set width to compare on next interval after half a second
                    width = $(window).width();
                }, 500);

            }else{
                //logging that should be removed in production
                console.log("function call " + numi++ + " and inteval set skipped");

            }

}

David Kamer
fonte