JavaScript / JQuery: $ (window) .resize comment tirer APRÈS le redimensionnement est terminé?

235

J'utilise JQuery en tant que tel:

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

Cependant, il semble que si la personne redimensionne manuellement les fenêtres de son navigateur en faisant glisser le bord de la fenêtre pour l'agrandir / la réduire, l' .resizeévénement ci-dessus se déclenche plusieurs fois.

Question: Comment appeler une fonction APRÈS le redimensionnement de la fenêtre du navigateur terminé (pour que l'événement ne se déclenche qu'une seule fois)?

Sarah
la source
Voir cette réponse: stackoverflow.com/questions/667426/… Cela implique l'utilisation de délais d'attente pour retarder l'exécution de votre fonction.
Alastair Pitts
Je ne sais pas si c'est possible, vous pouvez essayer ce plugin si benalman.com/projects/jquery-resize-plugin
BrunoLM
Soit dit en passant, j'ai remarqué que cela est très utile dans les navigateurs mobiles qui ont tendance à masquer progressivement la barre d'adresse lorsque l'utilisateur fait défiler vers le bas, redimensionnant ainsi l'écran. Je m'attendais également à ce que le redimensionnement de l'écran ne se produise que sur le bureau ...
MakisH

Réponses:

299

Voici une modification de la solution de CMS qui peut être appelée à plusieurs endroits de votre code:

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

Usage:

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

La solution de CMS est parfaite si vous ne l'appelez qu'une seule fois, mais si vous l'appelez plusieurs fois, par exemple si différentes parties de votre code configurent des rappels séparés pour le redimensionnement de la fenêtre, alors il échouera car ils partagent la timervariable.

Avec cette modification, vous fournissez un identifiant unique pour chaque rappel, et ces identifiants uniques sont utilisés pour garder tous les événements d'expiration séparés.

brahn
la source
2
Je <3 u, j'ai combiné cela tout en redimensionnant les graphiques Highcharts en plein écran (non html5) et fonctionne très bien.
Michael J. Calkins
Absolument incroyable!
Starkers
2
Puisque j'ai tellement profité de votre réponse, j'ai pensé partager un prototype fonctionnel de votre code fourni pour le reste de la communauté: jsfiddle.net/h6h2pzpu/3 . Profitez de tout le monde!
Jonas Stawski
1
Tout cela est parfait, MAIS cela retarde l'exécution de la fonction réelle d'un montant de millisecondes (ms), ce qui peut être TRÈS INDÉSIRABLE.
Tomas M
Cela est-il parfait? Existe-t-il un moyen de saisir le dernier redimensionnement et de ne pas avoir à relayer ces millisecondes? Quoi qu'il en soit, cela m'a sauvé la journée: ') Merci.
Leo
138

Je préfère créer un événement:

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

Voici comment vous le créez:

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

Vous pourriez avoir ceci dans un fichier javascript global quelque part.

Carlos Martinez T
la source
J'ai utilisé cette solution. Mais à long terme, je voudrais mettre en œuvre la même solution que brahn.
Bharat Patil
cela vous permet de vous lier à cet événement partout, pas seulement dans une fonction, en supposant que vous ayez plusieurs fichiers pages / .js nécessitant des réactions distinctes bien sûr
hanzolo
Cela n'a pas fonctionné pour moi, mais la réponse DusanV a fait le travail
lightbyte
123

J'utilise la fonction suivante pour retarder les actions répétées, cela fonctionnera pour votre cas:

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

Usage:

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

La fonction de rappel qui lui a été transmise ne s'exécutera que lorsque le dernier appel à retarder aura été effectué après la durée spécifiée, sinon une minuterie sera réinitialisée, je trouve cela utile à d'autres fins comme la détection du moment où l'utilisateur a arrêté de taper, etc. ..

CMS
la source
5
Je pense que cette approche peut échouer si elle est utilisée plusieurs fois (par exemple si différentes parties des rappels de configuration du code $(window).resize) car elles partageront toutes la timervariable. Voir ma réponse ci-dessous pour la solution proposée.
brahn
Très agréable! Fonctionne comme un charme! Comme vos réponses ... simple et élégant!
M. Pumpkin
74

Si Underscore.js est installé, vous pouvez:

$(window).resize(_.debounce(function(){
    alert("Resized");
},500));
JT.
la source
1
Même si vous ne voulez pas inclure Underscore, récupérez au moins la source pour cela: underscorejs.org/docs/underscore.html#section-67
tybro0103
Le rebounce est la bonne technique ici, c'est juste une question d'implémentations. Et c'est l'implémentation supérieure si Underscore / Lodash est déjà une dépendance de projet.
Factor Mystic
C'est maintenant ce que j'utilise,
Bharat Patil
20

Certaines des solutions mentionnées précédemment n'ont pas fonctionné pour moi, même si elles sont d'une utilisation plus générale. Alternativement, j'ai trouvé celui-ci qui a fait le travail sur le redimensionnement de la fenêtre:

$(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
la source
1
Seul votre exemple a fonctionné pour moi ... les autres ci-dessus ne l'ont pas fait! Je ne sais pas pourquoi votre réponse n'a pas été sélectionnée.
Mumbo Jumbo
Je ne comprends pas pourquoi attraper l'événement de redimensionnement à l'intérieur de l'événement de redimensionnement, mais cela fonctionne aussi pour moi ...
lightbyte
Mumbo Jumbo, je pense parce que d'autres se concentrent sur la généralisation, à la recherche d'une solution à un tel problème (appels multiples d'une fonction).
DusanV
lightbyte, c'est parce que vous devez arrêter les événements précédents à chaque appel de la fonction.
DusanV
13

Un grand merci à David Walsh, voici une version vanille de debounce de soulignement.

Code:

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

Utilisation simple:

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

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

Réf: http://davidwalsh.name/javascript-debounce-function

Irvin Dominin
la source
6

En fait, comme je le sais, vous ne pouvez pas faire certaines actions exactement lorsque le redimensionnement est désactivé, simplement parce que vous ne connaissez pas les actions des futurs utilisateurs. Mais vous pouvez supposer le temps écoulé entre deux événements de redimensionnement, donc si vous attendez un peu plus que ce temps et qu'aucun redimensionnement n'est effectué, vous pouvez appeler votre fonction.
L'idée est que nous utilisons setTimeoutet son identifiant afin de l'enregistrer ou de le supprimer. Par exemple, nous savons que le temps entre deux événements de redimensionnement est de 500 ms, nous attendrons donc 750 ms.

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

newint
la source
3

Déclarez l'auditeur globalement retardé:

var resize_timeout;

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

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

Et ci-dessous, utilisez des écouteurs pour l' resizedévénement comme vous le souhaitez:

$(window).on('resized', function(){
    console.log('resized');
});
gzzz
la source
Merci, c'est la meilleure solution actuellement. J'ai également testé que 250 ms fonctionnait le mieux lors du redimensionnement de la fenêtre.
AlexioVay
2

Plugin jQuery simple pour un événement de redimensionnement de fenêtre différé.

SYNTAXE:

Ajouter une nouvelle fonction pour redimensionner l'événement

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

Supprimez la fonction (en déclarant son ID) ajoutée précédemment

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

Supprimer toutes les fonctions

jQuery(window).resizeDelayed( false );

USAGE:

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

SORTIE D'UTILISATION:

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

BRANCHER:

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ć
la source
1

En supposant que le curseur de la souris revienne au document après le redimensionnement de la fenêtre, nous pouvons créer un comportement de rappel avec l'événement onmouseover. N'oubliez pas que cette solution peut ne pas fonctionner comme prévu pour les écrans tactiles.

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
la source
Bien pour les sites de bureau uniquement basés sur la souris, mais l'événement survol ne se produira jamais si, par exemple, l'utilisateur est sur un mobile et passe du paysage au portrait, ou s'il redimensionne la fenêtre sur un bureau à écran tactile.
user56reinstatemonica8
Vous pouvez redimensionner la fenêtre du navigateur à l'aide du clavier sur les dernières fenêtres comme Win-Arrow-Left Win-Arrow-Right, etc ...
Lu4
0

Voici ce que j'ai implémenté:

$ (fenêtre) .resize (function () {setTimeout (someFunction, 500);});

nous pouvons effacer le setTimeout si nous nous attendons à ce que le redimensionnement se produise moins de500ms

Bonne chance...

Akash
la source