¿Cómo esperar el 'final' del evento 'redimensionar' y solo luego realizar una acción?


243

Así que actualmente uso algo como:

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

Pero esto se llama muchas veces mientras continúa el proceso de cambio de tamaño. ¿Es posible atrapar un evento cuando termina?


¿Quizás adjuntar usando .one()para que solo se ejecute después de que se haya realizado el cambio de tamaño y no una y otra vez?
Brad Christie

55
Cuando un usuario cambia el tamaño de una ventana manualmente (arrastrándola), el evento de cambio de tamaño se llamará más de una vez, por lo que usar .one () realmente no será efectivo.
jessegavin

El uso de una función anónima en lo anterior podría eliminarse, por simplicidad y rapidez marginal: $ (ventana) .resize (resizedw)
Fornost

Aquí hay una biblioteca jQuery para esto: github.com/nielse63/jquery.resizeend
rugk

Respuestas:


177

Tuve suerte con la siguiente recomendación: http://forum.jquery.com/topic/the-resizeend-event

Aquí está el código para que no tenga que buscar en el enlace y la fuente de su publicación:

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

Gracias sime.vidas por el código!


1
Es posible que desee cambiar la fecha a algo así new Date(-1E12), es decir, JSLint advierte sobre el uso 00.
elundmark

Gracias elundmark. He cambiado la creación de instancias de fecha para usar 0 únicos; con suerte eso no generará una queja.
Dolan Antenucci

@elundmark o use + operación. rtime: Date; .... if (+new Date() - +rtime < delta)y en la función resizeend mecanografiada debe ser la función de flecha como esta resizeend=()=>. Porque en la función resizeend, thisreferencia al objeto de ventana.
Muhammet puede TONBUL

517

Puedes usar setTimeout()yclearTimeout()

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

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

Ejemplo de código en jsfiddle .


77
Esta es una respuesta genial. Hace lo que hace el complemento que recomendé, solo sin un complemento.
jessegavin

Creo que la única forma de mejorar realmente esto es detectar el movimiento del mouse. Sin embargo, sospecho que profundizar en ello no valdría la pena.
Michael Haren

¿Esto solo funciona si el cambio de tamaño finaliza en un segundo? Mi función se activaba cuando intenté usar esto (aunque era lento con el cambio de tamaño de mi ventana)
Dolan Antenucci

@MichaelHaren Como el controlador de cambio de tamaño generalmente está fuera del alcance $(document), la detección del mouse se limitaría a los usuarios que ejecutan Microsoft Windows y versiones vulnerables de su Internet Explorer: iedataleak.spider.io/demo
Alastair

12
Esta es una aplicación muy sencilla de la eliminación de rebote concepto ( unscriptable.com/2009/03/20/debouncing-javascript-methods ). Paul Irish (y otros) ha presentado una solución mucho más eficiente que no maneja eventos de cambio de tamaño 'innecesarios': paulirish.com/2009/throttled-smartresize-jquery-event-handler
rmoestl

78

Este es el código que escribo según la respuesta de @Mark Coleman:

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

Gracias Mark!


1
Buen aprobador. También se menciona aquí con la diferencia de que no se realizan modificaciones en la ventana súper variable .
Alwin Kesler

2
@AlwinKesler: en su ejemplo, la variable resizeTimeres una variable global, lo que significa que no está definida window, por lo que es exactamente la misma que aquí, solo que este ejemplo es mejor ya que no necesita definir la variable externamente. y también tiene sentido agregar esta variable al windowobjeto ya que ese es el objeto al que está vinculado el detector de eventos.
vsync

1
¡Gracias! Solo quería agregar que, en algunos casos, se necesita un intervalo de tiempo más largo para realizar ciertas tareas en una devolución de llamada. Por ejemplo, en mi caso, 250 no funcionaron, pero 700 funcionaron muy bien.
Maria Blair

La mejor solución.
Daniel Dewhurst

36

Internet Explorer proporciona un evento resizeEnd . Otros navegadores activarán el evento de cambio de tamaño muchas veces mientras cambia el tamaño.

Aquí hay otras excelentes respuestas que muestran cómo usar setTimeout y .throttle ,.debounce métodos de lodash y subrayado, por lo que mencionaré el plugin jQuery de Ben Alman para reducir y eliminar el rebote que logra lo que buscas.

Supongamos que tiene esta función que desea activar después de un cambio de tamaño:

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

Ejemplo de aceleración
En el siguiente ejemplo, onResize()solo se llamará una vez cada 250 milisegundos durante el cambio de tamaño de una ventana.

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

Ejemplo de rebote
En el siguiente ejemplo, onResize()solo se llamará una vez al final de una acción de cambio de tamaño de ventana. Esto logra el mismo resultado que @Mark presenta en su respuesta.

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

1
Lodash también es útil aquí, que también tiene los métodos _.throttle y _.debounce. Creo que debounce es un enfoque superior en comparación con el ejemplo aceptado anteriormente.
Kevin Leary

1
Sí, esta respuesta fue escrita hace más de 5 años. Han pasado muchas cosas desde los días del complemento jQuery. Aquí hay una función antirrebote independiente también davidwalsh.name/javascript-debounce-function
jessegavin


10

Una solución es extender jQuery con una función, por ejemplo: 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 de la muestra:

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


7

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

var loop = setInterval(func, 30);

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

Para hacer esto sin una variable "global", puede agregar una variable local a la función misma. Ex:

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

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

4

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 predeterminado on(& bind) -event-handler 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 de lo contrario. 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));

Utilizarlo como cualquier otro ono bind-Evento manipulador, excepto que se 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/


3

Hay un método mucho más simple para ejecutar una función al final del cambio de tamaño que calcular el tiempo delta entre dos llamadas, simplemente hazlo así:

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

function resizedEnded(){
    ...
}

Y el equivalente para Angular2 :

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

Para el método angular, use la () => { }notación en setTimeoutpara preservar el alcance, de lo contrario no podrá realizar llamadas a funciones ni usarlas this.


2

Esta es una modificación del código de Dolan anterior, agregué una función que verifica el tamaño de la ventana al comienzo del cambio de tamaño y lo compara con el tamaño al final del cambio de tamaño, si el tamaño es mayor o menor que el margen ( por ejemplo, 1000) luego se recarga.

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

2

La respuesta de Mark Coleman es ciertamente mucho mejor que la respuesta seleccionada, pero si desea evitar la variable global para el ID de tiempo de espera (la doitvariable en la respuesta de Mark), puede hacer una de las siguientes cosas:

(1) Use una expresión de función invocada inmediatamente (IIFE) para crear un cierre.

$(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 una propiedad de la función de controlador 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);
});

La opción 2, utilizando argumentos.callee, no funcionará si la función se transpila desde ES6.
Martin Burch

1

Escribí una pequeña función de envoltura por mi cuenta ...

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

Aquí está el uso:

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

// document ready
$(function() {
    onResize(testFn, 'argument1', 'argument2');
});

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

1

Bueno, en lo que respecta al administrador de ventanas, cada evento de cambio de tamaño es su propio mensaje, con un comienzo y un final distintos, por lo que técnicamente, cada vez que se cambia el tamaño de la ventana, es el final.

Habiendo dicho eso, ¿tal vez quieres retrasar tu continuación? Aquí hay un ejemplo.

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

1

Aquí hay un script MUY simple para desencadenar un evento 'resizestart' y 'resizeend' en el objeto de la ventana.

No hay necesidad de perder el tiempo con fechas y horas.

La dvariable representa el número de milisegundos entre los eventos de cambio de tamaño antes de activar el evento final de cambio de tamaño, puede jugar con esto para cambiar la sensibilidad del evento final.

Para escuchar estos eventos todo lo que necesita hacer es:

reiniciar: $(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));

1
Necesitaba el inicio y el final del cambio de tamaño, y parece que funciona bien (probado en Chrome, FF, Opera e IE11). Para probar, creé un JSFiddle con su solución: jsfiddle.net/8fsn2joj
Keith DC el

1

Esto es lo que uso para retrasar las acciones repetidas, se puede llamar en varios lugares en su 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);
});

0

dado que la respuesta seleccionada en realidad no funcionó ... y si no está usando jquery, aquí hay una función de acelerador simple con un ejemplo de cómo usarla con el cambio de tamaño de la ventana

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

0

esto funcionó para mí ya que no quería usar ningún complemento.

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

En el cambio de tamaño de la ventana, invoque "setFn", que obtiene el ancho de la ventana y lo guarda como "originalWindowSize". Luego invoque "checkFn", que después de 500 ms (o su preferencia) obtiene el tamaño actual de la ventana y compara el original con el actual, si no son iguales, entonces la ventana todavía se está redimensionando. No olvide eliminar los mensajes de la consola en producción, y (opcional) puede hacer que "setFn" se ejecute automáticamente.


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

Esto funcionó para lo que estaba tratando de hacer en Chrome. Esto no activará la devolución de llamada hasta 200 ms después del último evento de cambio de tamaño.


0

¡ACTUALIZAR!

Una mejor alternativa también creada por mí está aquí: https://stackoverflow.com/a/23692008/2829600 (admite "eliminar funciones")

POSTE ORIGINAL:

Escribí esta función simple para manejar el retraso en la ejecución, útil dentro de jQuery .scroll () y .resize () Entonces callback_f se ejecutará solo una vez para una cadena de identificación específica.

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!
*/

¿Podría aclarar el uso aquí? ¿Estás sugiriendo que uno hace $(window).resize(function() { delay_exec('test1', 30, function() { ... delayed stuff here ... }); });:? Código bastante limpio de lo contrario. Gracias por compartir. :)
mhulse

¡Tú Molas! Gracias @ Déján! +1 todo el camino. Ejemplo de código genial, funciona muy bien por lo que he probado. Fácil de usar, también. Gracias de nuevo por compartir. :)
mhulse

0

Eventos ResizeStart y ResizeEnd para ventana

http://jsfiddle.net/04fLy8t4/

Implementé la función que desencadena dos eventos en el elemento DOM del usuario:

  1. reiniciar
  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") );
};

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...
}

0

No sé si mi código funciona para otros, pero realmente hace un gran trabajo para mí. Tengo esta idea analizando el código de Dolan Antenucci porque su versión no me funciona y realmente espero que sea útil para alguien.

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

0

Escribí una función que pasa una función cuando se envuelve en cualquier evento de cambio de tamaño. Utiliza un intervalo para que el cambio de tamaño incluso no esté creando constantemente eventos de tiempo de espera. Esto le permite funcionar independientemente del evento de cambio de tamaño que no sea una entrada de registro que debe eliminarse en producción.

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

            }

}

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.