JavaScript / JQuery: $ (ventana) .resize cómo disparar DESPUÉS de completar el cambio de tamaño?

235

Estoy usando JQuery como tal:

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

Sin embargo, parece que si la persona redimensiona manualmente las ventanas de su navegador arrastrando el borde de la ventana para agrandarla / reducirla, el .resizeevento anterior se dispara varias veces.

Pregunta: ¿Cómo puedo llamar a una función DESPUÉS de que el tamaño de la ventana del navegador se haya completado (para que el evento solo se active una vez)?

sarah
fuente
Vea esta respuesta: stackoverflow.com/questions/667426/… Implica el uso de tiempos de espera para retrasar la ejecución de su función.
Alastair Pitts
No sé si eso es posible, puedes probar este complemento aunque benalman.com/projects/jquery-resize-plugin
BrunoLM
Por cierto, noté que esto es muy útil en los navegadores móviles que tienden a ocultar gradualmente la barra de direcciones cuando el usuario se desplaza hacia abajo, por lo tanto, cambia el tamaño de la pantalla. También esperaba que el cambio de tamaño de la pantalla ocurriera solo en el escritorio ...
MakisH

Respuestas:

299

Aquí hay una modificación de la solución de CMS que se puede llamar en varios lugares en su 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");
});

La solución de CMS está bien si solo lo llama una vez, pero si lo llama varias veces, por ejemplo, si diferentes partes de su código configuran devoluciones de llamada separadas para cambiar el tamaño de la ventana, entonces fallará porque comparten la timervariable.

Con esta modificación, proporciona una identificación única para cada devolución de llamada, y esas ID únicas se utilizan para mantener separados todos los eventos de tiempo de espera.

Brahn
fuente
2
I <3 u, combiné esto al cambiar el tamaño de los gráficos Highcharts a pantalla completa (no html5) y funciona muy bien.
Michael J. Calkins
¡Absolutamente increíble!
Starkers
2
Como me he beneficiado tanto de su respuesta, pensé que compartía un prototipo funcional de su código proporcionado para el resto de la comunidad: jsfiddle.net/h6h2pzpu/3 . ¡Disfruta de todos!
Jonas Stawski
1
Todo esto es perfecto, PERO retrasa la ejecución de la función real por la cantidad de milisegundos (ms), que puede ser MUY INDESEABLE.
Tomas M
Esto es perfecto. ¿Hay alguna forma de obtener el último cambio de tamaño y no tener que transmitir esos milisegundos? De todos modos, esto me salvó el día: ') Gracias.
Leo
138

Prefiero crear un evento:

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

Así es como lo creas:

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

Podría tener esto en un archivo javascript global en alguna parte.

Carlos Martinez T
fuente
Usé esta solución. Pero a largo plazo me gustaría implementar lo mismo que la solución de Brahn.
Bharat Patil
esto le permite vincularse a ese evento por todas partes, no solo en una función, suponiendo que tenga múltiples páginas / archivos .js que requieren reacciones separadas, por supuesto
hanzolo
Esto no funcionó para mí, pero la respuesta de DusanV hizo el trabajo
lightbyte
123

Utilizo la siguiente función para retrasar acciones repetidas, funcionará para su 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);
});

La función de devolución de llamada que se le pasó, se ejecutará solo cuando la última llamada al retraso se haya realizado después de la cantidad de tiempo especificada; de lo contrario, se restablecerá un temporizador, lo encuentro útil para otros fines, como detectar cuándo el usuario dejó de escribir, etc. ..

CMS
fuente
55
Creo que este enfoque puede fallar si se usa varias veces (por ejemplo, si diferentes partes de las devoluciones de llamada de configuración de código $(window).resize) porque todos compartirán la timervariable. Vea mi respuesta a continuación para la solución propuesta.
brahn
¡Muy agradable! ¡Funciona de maravilla! Al igual que sus respuestas ... simple y elegante!
Sr. Pumpkin
74

Si tiene instalado Underscore.js, podría:

$(window).resize(_.debounce(function(){
    alert("Resized");
},500));
JT
fuente
1
Incluso si no desea incluir el subrayado, al menos obtenga la fuente para esto: underscorejs.org/docs/underscore.html#section-67
tybro0103
Debounce es la técnica correcta aquí, solo es cuestión de implementaciones. Y esta es la implementación superior si Underscore / Lodash ya es una dependencia del proyecto.
Factor Mystic
Esto es ahora lo que estoy usando,
Bharat Patil
20

Algunas de las soluciones mencionadas anteriormente no funcionaron para mí, a pesar de que son de uso más general. Alternativamente, he encontrado este que hizo el trabajo en cambiar el tamaño de la ventana:

$(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
fuente
1
Solo su ejemplo funcionó para mí ... ¡los otros anteriores no! No estoy seguro de por qué su respuesta no fue seleccionada.
Mumbo Jumbo
No entiendo por qué a capturar el evento de cambio de tamaño en el caso de cambio de tamaño, pero funciona para mí también ...
lightbyte
Mumbo Jumbo, creo que porque otros se centran en la generalización, buscando la solución a cualquier problema (llamadas múltiples de una función).
DusanV
lightbyte, es porque tienes que detener eventos anteriores cada vez que se llama a la función.
DusanV
13

Muchas gracias a David Walsh, aquí hay una versión vainilla del subrayado de rebote.

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 simple:

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

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

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

Irvin Dominin
fuente
6

En realidad, como sé, no puede realizar algunas acciones exactamente cuando el cambio de tamaño está desactivado, simplemente porque no conoce las acciones de futuros usuarios. Pero puede suponer el tiempo transcurrido entre dos eventos de cambio de tamaño, por lo que si espera un poco más que este tiempo y no se realiza ningún cambio de tamaño, puede llamar a su función.
La idea es que usamos setTimeouty es id para guardarlo o eliminarlo. Por ejemplo, sabemos que el tiempo entre dos eventos de cambio de tamaño es de 500 ms, por lo tanto, esperaremos 750 ms.

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

novedad
fuente
3

Declarar oyente globalmente retrasado:

var resize_timeout;

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

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

Y a continuación, use oyentes para el resizedevento que desee:

$(window).on('resized', function(){
    console.log('resized');
});
gzzz
fuente
Gracias, esa es la mejor solución actualmente. También he probado que 250 ms funciona de la mejor manera al cambiar el tamaño de la ventana.
AlexioVay
2

Complemento jQuery simple para el evento de cambio de tamaño de ventana demorado

SINTAXIS:

Agregar nueva función para cambiar el tamaño del evento

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

Elimine la función (declarando su ID) agregada anteriormente

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

Eliminar todas las funciones

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

SALIDA 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

ENCHUFAR:

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ć
fuente
1

Suponiendo que el cursor del mouse regrese al documento después de cambiar el tamaño de la ventana, podemos crear un comportamiento similar al de la devolución de llamada con el evento onmouseover. No olvide que esta solución puede no funcionar para pantallas táctiles como se esperaba.

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
fuente
Está bien para sitios de escritorio basados ​​en mouse, pero el evento mouseover nunca sucederá si, por ejemplo, el usuario está en un dispositivo móvil y cambia de horizontal a vertical, o si cambia el tamaño de la ventana en un escritorio con pantalla táctil.
user56reinstatemonica8
Puede cambiar el tamaño de la ventana del navegador con la ayuda del teclado en las ventanas más recientes como Win-Arrow-Left Win-Arrow-Right, etc ...
Lu4
0

Esto es lo que he implementado:

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

podemos borrar el setTimeout si esperamos que el cambio de tamaño suceda menos de500ms

Buena suerte...

Akash
fuente