JQuery: ¿Cómo llamar al evento RESIZE solo una vez que TERMINE el cambio de tamaño?

109

¿Cómo llamo a una función una vez que las ventanas del navegador TERMINARON de cambiar el tamaño?

Estoy tratando de hacerlo así, pero tengo problemas. Estoy usando la función de evento JQuery Resize:

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

Sin embargo, esta función se llama continuamente si el usuario cambia manualmente el tamaño de la ventana del navegador. Lo que significa que podría llamar a esta función docenas de veces en un corto intervalo de tiempo.

¿Cómo puedo llamar a la función de cambio de tamaño solo una vez (una vez que la ventana del navegador ha terminado de cambiar el tamaño)?

ACTUALIZAR

También sin tener que utilizar una variable global.

nickb
fuente
@BGerrissen, si puedes mostrarme cómo hacer jsfiddle.net/Zevan/c9UE5/1 sin una variable global, definitivamente lo haré :)
nickb
el método anterior cleartimeout / settimeout funciona maravillosamente bien.
kris-o3

Respuestas:

129

Aquí hay un ejemplo usando las instrucciones de thejh

Puede almacenar una identificación de referencia para cualquier setInterval o setTimeout. Me gusta esto:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);
Zevan
fuente
Me encanta que este código sea tan simple. ¿Alguna forma de hacer que esto funcione sin tener que usar una variable global?
nickb
Si veo cómo hacer esto sin la variable global, lo convertiré en la "respuesta aceptada"
nickb
No hay problema. Hay algunas maneras. He editado la respuesta para reflejar la más simple que se me ocurrió.
Zevan
gracias por la actualización, pero parece que todavía usa una variable global. ¿Puede actualizarlo para que no requiera una variable global (var id)? Gracias
nickb
4
Creo que te perdiste el enlace al final de la publicación ... compruébalo: jsfiddle.net/Zevan/c9UE5/5
Zevan
85

Rebote .

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

Tenga en cuenta que puede usar este método para cualquier cosa que desee eliminar (eventos clave, etc.).

Modifique el parámetro de tiempo de espera para obtener el efecto deseado óptimo.

BGerrissen
fuente
1
Intenté ejecutar esto y parece que se ejecuta DOS VECES . Reemplacé "// hacer cosas" con "$ (" cuerpo "). Append (" <br/> ¡HECHO! ");" y lo llama DOS VECES en un cambio de tamaño del navegador
nickb
Ups ... Olvidé una parte bastante importante (en realidad, estableciendo el timeoutID) ... Arreglado ahora, inténtelo de nuevo;)
BGerrissen
3
@ user43493: llama a cuntion func, con el thispuntero interno apuntando hacia scopey con Array.prototype.slice.call(args)(que genera una matriz estándar args) como argumentos
Eric
4
Esta es una abstracción reutilizable, por lo que no tiene que codificar manualmente los tiempos de espera o realizar un seguimiento de los identificadores de tiempo de espera globales usted mismo. Puede usarlo para mucho más que solo cambiar el tamaño de la ventana;) por ejemplo, para eliminar el rebote de un botón de envío pasando un parámetro de tiempo de espera más alto. Puede optar por la solución con menos código, pero le aconsejo que mantenga este fragmento en su kit, lo apreciará más adelante;)
BGerrissen
2
Underscore.js tiene una buena implementación de esto si ya está usando esa lib. underscorejs.org/#debounce
twmulloy
22

Puede usar setTimeout()y clearTimeout()junto con jQuery.data:

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

Actualizar

Escribí una extensión para mejorar el controlador de eventos predeterminado on(& bind) de jQuery. Adjunta una función de controlador de eventos para uno o más eventos a los elementos seleccionados si el evento no se activó durante un intervalo determinado. Esto es útil si desea activar una devolución de llamada solo después de un retraso, como el evento de cambio de tamaño, o bien. 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));

Úselo como cualquier otro controlador de eventos ono bind, excepto que puede pasar un parámetro adicional como último:

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

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

yckart
fuente
1
Literalmente, haz la misma búsqueda en Google todos los días. y vaya a la misma página para este fragmento de código. Ojalá pudiera recordarlo jaja.
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
fuente
Esto es lo que más me gusta, pero ¿por qué no usar llaves en sus declaraciones condicionales? Sé que funciona sin ellos, pero es una molestia para otros desarrolladores mirar;)
teewuane
7

Solo para agregar a lo anterior, es común obtener eventos de cambio de tamaño no deseados debido a que las barras de desplazamiento aparecen y desaparecen, aquí hay un código para evitar eso:

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

ver jsfiddle

kofifus
fuente
5

Underscore.js tiene un par de métodos excelentes para esta tarea: throttley debounce. Incluso si no está utilizando Underscore, eche un vistazo a la fuente de estas funciones. He aquí un ejemplo:

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

Este es mi enfoque:

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 captura todas las llamadas entrantes de cambio de tamaño, crea una función de tiempo de espera para cada una y guarda el identificador de tiempo de espera junto con un número iterativo antepuesto por 'id-'(para ser utilizable como clave de matriz) en la tosmatriz -array.

cada vez, el tiempo de espera se dispara, llama a la función fn-que comprueba si ese fue el último tiempo de espera en la tosmatriz (la función fn elimina cada tiempo de espera ejecutado). si es verdadero (= if(len-1 == 0)), el cambio de tamaño ha finalizado.

lsblsb
fuente
Sería bueno si tuviera algunos comentarios o una explicación de lo que está haciendo aquí
Brett Gregson
1

jQuery proporciona un offmétodo para eliminar el controlador de eventos

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