617 votes

Exécuter la fonction javascript quand l'utilisateur a fini de taper au lieu de le faire quand il appuie sur la touche ?

Je veux déclencher une requête ajax lorsque l'utilisateur a fini de taper dans une zone de texte. Je ne veux pas que la fonction soit exécutée à chaque fois que l'utilisateur tape une lettre, car cela entraînerait BEAUCOUP de requêtes ajax, mais je ne veux pas non plus qu'il doive appuyer sur le bouton d'entrée.

Existe-t-il un moyen de détecter quand l'utilisateur a fini de taper et d'effectuer ensuite la requête ajax ?

Utilisation de jQuery ici ! Dave

27 votes

Je pense que vous allez devoir définir "finir de taper" pour nous.

8 votes

Bien que la réponse de @Surreal Dreams satisfasse la plupart de vos exigences, si l'utilisateur recommence à taper APRÈS le délai spécifié, de multiples requêtes seront envoyées au serveur. Voir ma réponse ci-dessous qui stocke chaque requête XHR dans une variable et l'annule avant d'en envoyer une nouvelle. C'est en fait ce que fait Google dans son Instantané recherche.

0 votes

La réponse choisie est incorrecte pour plusieurs raisons : 1. Il se déclenche toujours après 5 secondes, même si l'utilisateur est en train de taper. 2. Il n'attend pas que l'utilisateur ait fini de taper comme demandé. 3. Il lance des requêtes multiples comme mentionné par @Marko ci-dessus. Voir ma réponse corrigée ci-dessous.

821voto

Surreal Dreams Points 12016

Donc, je vais supposer que finir de taper signifie que vous vous arrêtez pendant un moment, disons 5 secondes. Donc, avec cela à l'esprit, nous allons démarrer un minuteur lorsque l'utilisateur relâche une touche et l'effacer lorsqu'il en appuie une. J'ai décidé que l'entrée en question serait #myInput.

Je fais quelques hypothèses...

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms, 5 second for example
var $input = $('#myInput');

//on keyup, start the countdown
$input.on('keyup', function () {
  clearTimeout(typingTimer);
  typingTimer = setTimeout(doneTyping, doneTypingInterval);
});

//on keydown, clear the countdown 
$input.on('keydown', function () {
  clearTimeout(typingTimer);
});

//user is "finished typing," do something
function doneTyping () {
  //do something
}

4 votes

Merci :) J'ai commencé à taper mon commentaire sur la question et j'ai réalisé que j'avais une bonne idée.

0 votes

Désolé d'être un emmerdeur, mais il y a deux fois keyup au lieu de keydown sur le 2ème événement, et textbox n'est pas un sélecteur valide puisqu'il n'y a pas de textbox élément :)

0 votes

Vous avez keyup faute de frappe au lieu de keydown

480voto

xiaohouzi79 Points 3165

La réponse choisie ci-dessus ne fonctionne pas.

Parce que typingTimer est parfois activé plusieurs fois (touche pressée deux fois avant que touche pressée ne soit déclenchée pour les dactylographes rapides, etc.), il ne s'efface pas correctement.

La solution ci-dessous résout ce problème et appelle X secondes après la fin de l'opération, comme demandé par le PO. Elle ne nécessite plus non plus la fonction redondante keydown. J'ai également ajouté une vérification pour que l'appel de la fonction ne se produise pas si votre entrée est vide.

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms (5 seconds)

//on keyup, start the countdown
$('#myInput').keyup(function(){
    clearTimeout(typingTimer);
    if ($('#myInput').val()) {
        typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

Et le même code dans la solution vanilla JavaScript :

//setup before functions
let typingTimer;                //timer identifier
let doneTypingInterval = 5000;  //time in ms (5 seconds)
let myInput = document.getElementById('myInput');

//on keyup, start the countdown
myInput.addEventListener('keyup', () => {
    clearTimeout(typingTimer);
    if (myInput.value) {
        typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

Cette solution utilise ES6 mais ce n'est pas nécessaire ici. Il suffit de remplacer let avec var et la fonction flèche avec une fonction régulière.

0 votes

Je crois que le problème que vous rencontrez est que vous tapez assez vite pour appuyer sur la deuxième touche avant de relâcher la première.

1 votes

Ce n'est pas un problème qu'il rencontre, c'est un problème que tout utilisateur peut reproduire comme je viens de le faire. Imaginez qu'à la fin du délai d'attente, vous renvoyez un message ajax au serveur et faites quelque chose avec la réponse. La réponse acceptée aurait été postée à plusieurs reprises pour moi. Mettez une alerte et tapez très vite. Cela ne se déclenchera pas seulement à la fin du délai d'attente, pour moi, cela s'est déclenché à plusieurs reprises. C'est la réponse la plus complète, et elle utilise moins de code, bravo.

7 votes

Il pourrait être utile d'appeler la fonction même si l'entrée est vide. Que faire si vous voulez effacer certains résultats de recherche (par exemple) lorsque l'entrée est à nouveau vide ?

89voto

bit2pixel Points 94

C'est juste une ligne avec underscore.js fonction de débordement :

$('#my-input-box').keyup(_.debounce(doSomething , 500));

Cela dit en gros fairequelquechose 500 millisecondes après que j'ai arrêté de taper.

Pour plus d'informations : http://underscorejs.org/#debounce

7 votes

Il semble qu'il soit également disponible pour jQuery : code.google.com/p/jquery-debounce et github.com/diaspora/jquery-debounce

117 votes

Je suis étonné de voir comment les gens vantent "UNE LIGNE DE CODE" comme étant d'une importance capitale. Super. UNE LIGNE de code qui nécessite un fichier JS de 1.1k pour être chargé. Je ne vote pas contre cette solution parce que c'est une solution. Mais l'idée d'une ligne en gras m'irrite, tout comme le fait qu'elle conduise à un code trop lourd.

9 votes

@Wolfie, je suis d'accord avec vous au sujet du gonflement du code, mais Underscore et Lodash sont les deux utilitaires les plus utilisés dans l'entreprise. NPM Il s'agit donc d'une solution unique pour de nombreuses personnes.

48voto

Marko Points 26030

Oui, vous pouvez définir un délai d'attente de 2 secondes, par exemple, pour chaque événement de frappe qui déclenchera une requête ajax. Vous pouvez également stocker la méthode XHR et l'interrompre sur les événements de pression de touche suivants afin d'économiser encore plus de bande passante. Voici quelque chose que j'ai écrit pour un script d'autocomplétion.

var timer;
var x;

$(".some-input").keyup(function () {
    if (x) { x.abort() } // If there is an existing XHR, abort it.
    clearTimeout(timer); // Clear the timer so we don't end up with dupes.
    timer = setTimeout(function() { // assign timer a new timeout 
        x = $.getJSON(...); // run ajax request and store in x variable (so we can cancel)
    }, 2000); // 2000ms delay, tweak for faster/slower
});

J'espère que cela vous aidera,

Marko

1 votes

Je ne le recommande pas. Le code déclenche une demande d'API à chaque fois une touche est enfoncée. Puis il annule la demande si une autre touche est enfoncée. Bien que cette solution empêche le déclenchement du callback AJAX pendant que l'utilisateur tape, le serveur sera tout de même submergé de requêtes.

0 votes

Ixg, non, ça ne marchera pas. La fonction clearTimeout qu'il utilise efface les événements Timeout précédents, invoquant ainsi l'appel API.

0 votes

@JohnSmith, je ne suis pas d'accord, c'est probablement mieux d'effacer d'abord le timer, puis de vérifier si xhr est vide ou n'importe quelle autre condition, et enfin de lancer le timer avec l'appel ajax.

22voto

SubstanceD Points 926
var timer;
var timeout = 1000;

$('#in').keyup(function(){
    clearTimeout(timer);
    if ($('#in').val) {
        timer = setTimeout(function(){
            //do stuff here e.g ajax call etc....
             var v = $("#in").val();
             $("#out").html(v);
        }, timeout);
    }
});

exemple complet ici : http://jsfiddle.net/ZYXp4/8/

0 votes

Cela a fonctionné pour moi, mais gardez à l'esprit que lorsque l'utilisateur quitte la zone de texte avec la tabulation, vous n'obtiendrez pas d'événement keyup car la zone a perdu le focus à ce moment-là. Le réglage de l'événement keydown semble résoudre le problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X