web-dev-qa-db-fra.com

Comment retarder le gestionnaire .keyup () jusqu'à ce que l'utilisateur cesse de taper?

J'ai un champ de recherche. En ce moment, il cherche chaque keyup. Donc si quelqu'un tape “Windows”, il fera une recherche avec AJAX pour chaque keyup: “W”, “Wi”, “Win”, “Wind”, “Windo”, “Window”, “Windows” .

Je souhaite un délai, de sorte qu'il recherche uniquement lorsque l'utilisateur cesse de taper pendant 200 ms.

Il n’existe pas d’option pour cela dans la fonction keyup et j’ai essayé setTimeout, mais cela n’a pas fonctionné.

Comment puis je faire ça?

567
ajsie

J'utilise cette petite fonction dans le même but, en l'exécutant une fois que l'utilisateur a cessé de taper pendant un laps de temps spécifié ou dans des événements qui se déclenchent à une vitesse élevée, comme resize:

function delay(callback, ms) {
  var timer = 0;
  return function() {
    var context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      callback.apply(context, args);
    }, ms || 0);
  };
}


// Example usage:

$('#input').keyup(delay(function (e) {
  console.log('Time elapsed!', this.value);
}, 500));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="input">Try it:
<input id="input" type="text" placeholder="Type something here..."/>
</label>

Comment ça marche:

La fonction delay renverra une fonction encapsulée qui gère en interne un temporisateur individuel. Lors de chaque exécution, le temporisateur est redémarré avec le délai imparti. Si plusieurs exécutions ont lieu avant ce délai, le temporisateur se réinitialise et redémarre.

Lorsque le temporisateur se termine enfin, la fonction de rappel est exécutée en transmettant le contexte et les arguments d'origine (dans cet exemple, l'objet événement de jQuery et l'élément DOM sous la forme this).

Pour quelque chose de plus sophistiqué, jetez un coup d’œil au plugin jQuery Typewatch .

997
CMS

Si vous souhaitez effectuer une recherche une fois le type défini, utilisez une variable globale pour conserver le délai d'attente renvoyé par votre appel setTimout et annulez-le avec une variable clearTimeout s'il n'a pas encore eu lieu, de sorte qu'il ne déclenche pas le délai d'expiration sauf le dernier paramètre keyup. un événement

var globalTimeout = null;  
$('#id').keyup(function(){
  if(globalTimeout != null) clearTimeout(globalTimeout);  
  globalTimeout =setTimeout(SearchFunc,200);  
}   
function SearchFunc(){  
  globalTimeout = null;  
  //ajax code
}

Ou avec une fonction anonyme:

var globalTimeout = null;  
$('#id').keyup(function() {
  if (globalTimeout != null) {
    clearTimeout(globalTimeout);
  }
  globalTimeout = setTimeout(function() {
    globalTimeout = null;  

    //ajax code

  }, 200);  
}   
54
RHicke

Une autre légère amélioration sur la réponse de CMS. Pour permettre facilement des délais distincts, vous pouvez utiliser les éléments suivants:

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

Si vous voulez réutiliser le même délai, faites juste

var delay = makeDelay(250);
$(selector1).on('keyup', function() {delay(someCallback);});
$(selector2).on('keyup', function() {delay(someCallback);});

Si vous voulez des délais séparés, vous pouvez faire

$(selector1).on('keyup', function() {makeDelay(250)(someCallback);});
$(selector2).on('keyup', function() {makeDelay(250)(someCallback);});
29
Paschover

Vous pouvez également consulter underscore.js , qui fournit des méthodes utilitaires telles que debounce :

var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);
27
meleyal

Basé sur la réponse de CMS, j'ai fait ceci:

Mettez le code ci-dessous après inclure jQuery:

/*
 * delayKeyup
 * http://code.azerti.net/javascript/jquery/delaykeyup.htm
 * Inspired by CMS in this post : http://stackoverflow.com/questions/1909441/jquery-keyup-delay
 * Written by Gaten
 * Exemple : $("#input").delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);
 */
(function ($) {
    $.fn.delayKeyup = function(callback, ms){
        var timer = 0;
        $(this).keyup(function(){                   
            clearTimeout (timer);
            timer = setTimeout(callback, ms);
        });
        return $(this);
    };
})(jQuery);

Et utilisez simplement comme ceci:

$('#input').delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);

Attention: la variable $ (this) de la fonction passée en paramètre ne correspond pas à l'entrée

14
Gaten

Cette fonction étend un peu la fonction de la réponse de Gaten afin de récupérer l'élément:

$.fn.delayKeyup = function(callback, ms){
    var timer = 0;
    var el = $(this);
    $(this).keyup(function(){                   
    clearTimeout (timer);
    timer = setTimeout(function(){
        callback(el)
        }, ms);
    });
    return $(this);
};

$('#input').delayKeyup(function(el){
    //alert(el.val());
    // Here I need the input element (value for ajax call) for further process
},1000);

http://jsfiddle.net/Us9bu/2/

6
fazzyx

Cela a fonctionné pour moi où j'ai retardé l'opération de logique de recherche et vérifié si la valeur est identique à celle entrée dans le champ de texte. Si la valeur est identique, j'effectue l'opération pour les données liées à la valeur de recherche.

$('#searchText').on('keyup',function () {
    var searchValue = $(this).val();
    setTimeout(function(){
        if(searchValue == $('#searchText').val() && searchValue != null && searchValue != "") {
           // logic to fetch data based on searchValue
        }
        else if(searchValue == ''){
           // logic to load all the data
        }
    },300);
});
6
Sagar Gala

Si quelqu'un aime retarder la même fonction et sans variable externe, il peut utiliser le script suivant:

function MyFunction() {

    //Delaying the function execute
    if (this.timer) {
        window.clearTimeout(this.timer);
    }
    this.timer = window.setTimeout(function() {

        //Execute the function code here...

    }, 500);
}
5
Roy Shoa

Je suis surpris que personne ne mentionne le problème de la saisie multiple dans le très joli CMS coupé par CMS.

En gros, vous devez définir une variable de délai individuellement pour chaque entrée. Sinon, si sb met du texte dans la première entrée et passe rapidement à une autre entrée et commence à taper, rappelle pour la première WON'T be call!

Voir le code ci-dessous je suis venu avec basé sur d'autres réponses:

(function($) {
    /**
     * KeyUp with delay event setup
     * 
     * @link http://stackoverflow.com/questions/1909441/jquery-keyup-delay#answer-12581187
     * @param function callback
     * @param int ms
     */
    $.fn.delayKeyup = function(callback, ms){
            $(this).keyup(function( event ){
                var srcEl = event.currentTarget;
                if( srcEl.delayTimer )
                    clearTimeout (srcEl.delayTimer );
                srcEl.delayTimer = setTimeout(function(){ callback( $(srcEl) ); }, ms);
            });

        return $(this);
    };
})(jQuery);

Cette solution conserve la référence setTimeout dans la variable delayTimer de l'entrée. Il transmet également la référence de l'élément au rappel comme suggéré par fazzyx.

Testé dans IE6, 8 (comp - 7), 8 et Opera 12.11.

5
jszoja

Retarder la fonction pour appeler sur chaque keyup . jQuery 1.7.1 ou supérieur requis

jQuery.fn.keyupDelay = function( cb, delay ){
  if(delay == null){
    delay = 400;
  }
  var timer = 0;
  return $(this).on('keyup',function(){
    clearTimeout(timer);
    timer = setTimeout( cb , delay );
  });
}

Usage: $('#searchBox').keyupDelay( cb );

4
Vinay Aggarwal

Approche super simple, conçue pour exécuter une fonction après qu'un utilisateur a fini de taper dans un champ de texte ...

<script type="text/javascript">
$(document).ready(function(e) {
    var timeout;
    var delay = 2000;   // 2 seconds

    $('.text-input').keyup(function(e) {
        console.log("User started typing!");
        if(timeout) {
            clearTimeout(timeout);
        }
        timeout = setTimeout(function() {
            myFunction();
        }, delay);
    });

    function myFunction() {
        console.log("Executing function for user!");
    }
});
</script>

<textarea name="text-input" class="text-input"></textarea>
4
HoldOffHunger

En se basant sur la réponse de CMS, voici une nouvelle méthode de délai qui préserve "ceci" dans son utilisation:

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

Usage:

$('input').keyup(function() {
    delay(function(){
      alert('Time elapsed!');
    }, 1000, this);
});
3
mga911

C’est une solution qui ressemble au système de gestion de contenu, mais résout quelques problèmes clés pour moi:

  • Prend en charge plusieurs entrées, les retards peuvent s'exécuter simultanément.
  • Ignore les événements clés qui n'ont pas changé la valeur (comme Ctrl, Alt + Tab).
  • Résout une situation de concurrence critique (lorsque le rappel est exécuté et que la valeur a déjà changé).
var delay = (function() {
    var timer = {}
      , values = {}
    return function(el) {
        var id = el.form.id + '.' + el.name
        return {
            enqueue: function(ms, cb) {
                if (values[id] == el.value) return
                if (!el.value) return
                var original = values[id] = el.value
                clearTimeout(timer[id])
                timer[id] = setTimeout(function() {
                    if (original != el.value) return // solves race condition
                    cb.apply(el)
                }, ms)
            }
        }
    }
}())

Usage:

signup.key.addEventListener('keyup', function() {
    delay(this).enqueue(300, function() {
        console.log(this.value)
    })
})

Le code est écrit dans un style qui me convient, il se peut que vous deviez ajouter un groupe de points-virgules.

Points à garder à l'esprit:

  • Un identifiant unique est généré en fonction de l'identifiant du formulaire et du nom de l'entrée. Ils doivent donc être définis et uniques, ou vous pouvez les ajuster à votre situation.
  • delay retourne un objet facile à étendre pour vos propres besoins.
  • L'élément d'origine utilisé pour delay est lié au rappel, donc this fonctionne comme prévu (comme dans l'exemple).
  • La valeur vide est ignorée lors de la deuxième validation.
  • Faites attention à enqueue , il s’attend d’abord en millisecondes, mais je préférerais cela, mais vous voudrez peut-être changer les paramètres pour faire correspondre à setTimeout.

La solution que j'utilise ajoute un autre niveau de complexité, vous permettant par exemple d’annuler l’exécution, mais c’est une bonne base de travail.

2
Jaime Gómez

En combinant la réponse CMS avec celle de Miguel , vous obtenez une solution robuste permettant des retards simultanés.

var delay = (function(){
    var timers = {};
    return function (callback, ms, label) {
        label = label || 'defaultTimer';
        clearTimeout(timers[label] || 0);
        timers[label] = setTimeout(callback, ms);
    };
})();

Lorsque vous devez retarder différentes actions indépendamment, utilisez le troisième argument.

$('input.group1').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, 'firstAction');
});

$('input.group2').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, '2ndAction');
});
2
Frédéric

Basé sur la réponse du CMS, il ignore simplement les événements clés qui ne changent pas de valeur. 

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

var duplicateFilter=(function(){
  var lastContent;
  return function(content,callback){
    content=$.trim(content);
    if(content!=lastContent){
      callback(content);
    }
    lastContent=content;
  };
})();

$("#some-input").on("keyup",function(ev){

  var self=this;
  delay(function(){
    duplicateFilter($(self).val(),function(c){
        //do sth...
        console.log(c);
    });
  }, 1000 );


})
2
Anderson

Utilisation 

mytimeout = setTimeout( expression, timeout );

où expression est le script à exécuter et timeout, le délai d'attente, en millisecondes, avant de s'exécuter - cela ne perturbe PAS le script, mais retarde simplement l'exécution de cette partie jusqu'à la fin du délai.

clearTimeout(mytimeout);

réinitialisera/effacera le délai afin de ne pas exécuter le script dans une expression (comme une annulation) tant qu'il n'a pas encore été exécuté.

2
Mark Schultheiss

Utilisez le plugin bindWithDelay jQuery:

element.bindWithDelay(eventType, [ eventData ], handler(eventObject), timeout, throttle)
1
Vebjorn Ljosa

Voici une suggestion que j'ai écrite et qui prend en charge plusieurs entrées dans votre formulaire.

Cette fonction récupère l'objet du champ de saisie, mis dans votre code

function fieldKeyup(obj){
    //  what you want this to do

} // fieldKeyup

C'est la fonction delayCall réelle, prend en charge plusieurs champs de saisie

function delayCall(obj,ms,fn){
    return $(obj).each(function(){
    if ( typeof this.timer == 'undefined' ) {
       // Define an array to keep track of all fields needed delays
       // This is in order to make this a multiple delay handling     
          function
        this.timer = new Array();
    }
    var obj = this;
    if (this.timer[obj.id]){
        clearTimeout(this.timer[obj.id]);
        delete(this.timer[obj.id]);
    }

    this.timer[obj.id] = setTimeout(function(){
        fn(obj);}, ms);
    });
}; // delayCall

Usage:

$("#username").on("keyup",function(){
    delayCall($(this),500,fieldKeyup);
});
1
egpo
var globalTimeout = null;  
$('#search').keyup(function(){
  if(globalTimeout != null) clearTimeout(globalTimeout);  
  globalTimeout =setTimeout(SearchFunc,200);  
});
function SearchFunc(){  
  globalTimeout = null;  
  console.log('Search: '+$('#search').val());
  //ajax code
};
1
krut1

Eh bien, j'ai également fait un morceau de code pour limiter cause haute demande ajax par Keyup/Keydown. Regarde ça:

https://github.com/raincious/jQueue

Faites votre requête comme ceci:

var q = new jQueue(function(type, name, callback) {
    return $.post("/api/account/user_existed/", {Method: type, Value: name}).done(callback);
}, 'Flush', 1500); // Make sure use Flush mode.

Et lier un événement comme ceci:

$('#field-username').keyup(function() {
    q.run('Username', this.val(), function() { /* calling back */ });
});
0
user2328799

Bibliothèque utilisateur lodash javascript et utilisation de la fonction _.debounce

changeName: _.debounce(function (val) {
  console.log(val)                
}, 1000)
0
Amir Ur Rehman

Nous avons vu cela aujourd'hui un peu tard, mais je voulais simplement mettre ceci ici au cas où quelqu'un d'autre en aurait besoin. séparez simplement la fonction pour la rendre réutilisable. le code ci-dessous attendra 1/2 seconde après avoir tapé stop.

    var timeOutVar
$(selector).on('keyup', function() {

                    clearTimeout(timeOutVar);
                    timeOutVar= setTimeout(function(){ console.log("Hello"); }, 500);
                });
0
chungtinhlakho

Jetez un coup d'œil au autocomplete plugin. Je sais que cela vous permet de spécifier un délai ou un nombre minimum de caractères. Même si vous n'utilisez pas le plugin, parcourir le code vous donnera quelques idées pour l'implémenter vous-même.

0
tvanfosson

A partir de ES6, vous pouvez utiliser la syntaxe de la fonction de flèche

Dans cet exemple, le code retarde l'événement keyup pendant 400 ms après que les utilisateurs aient fini de taper avant d'appeler searchFunc de faire une demande de requête.

const searchbar = document.getElementById('searchBar');
const searchFunc = // any function

// wait ms (milliseconds) after user stops typing to execute func
const delayKeyUp = (() => {
    let timer = null;
    const delay = (func, ms) => {
        timer ? clearTimeout(timer): null
        timer = setTimeout(func, ms)
    }
    return delay
})();

searchbar.addEventListener('keyup', (e) => {
    const query = e.target.value;
    delayKeyUp(() => {searchFunc(query)}, 400);
})
0
Dat Nguyen