web-dev-qa-db-fra.com

Exécuter la fonction javascript lorsque l'utilisateur a fini de taper au lieu de la touche suivante

Je souhaite déclencher une demande ajax lorsque l'utilisateur a fini de taper dans une zone de texte. Je ne veux pas que la fonction soit activée à chaque fois que l'utilisateur tape une lettre car cela entraînerait BEAUCOUP de demandes en ajax, mais je ne veux pas qu'ils soient obligés d'appuyer sur le bouton entrée.

Existe-t-il un moyen de détecter le moment où l'utilisateur a fini de taper, puis de faire la demande ajax?

Utilisation de jQuery ici! Dave

384
Macmee

Donc, je suppose que la fin de la frappe signifie que vous vous arrêtez un moment, disons 5 secondes. Donc, gardons cela à l’esprit, permet de démarrer une minuterie lorsque l’utilisateur relâche une touche et de l’effacer lorsqu’il appuie sur une touche. J'ai décidé que l'entrée en question serait #myInput.

Faire 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
}
565
Surreal Dreams

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

Du fait que typingTimer est défini occasionnellement à plusieurs reprises (keyup enfoncé deux fois avant le déclenchement de keydown pour les typers 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, à la demande de l'OP. De plus, il n’exige plus la fonction de raccourci clavier redondant. J'ai également ajouté un contrôle pour que votre appel de 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 JavaScript Vanilla:

//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 par var et la fonction de flèche par une fonction régulière.

325
going

C'est juste une ligne avec underscore.js fonction anti-rebond:

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

Cela signifie en gros doQuelque chose 500 millisecondes après avoir cessé de taper.

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

70
bit2pixel

Oui, vous pouvez définir un délai d'attente de, disons, 2 secondes pour chaque événement clé haut qui déclenchera une demande ajax. Vous pouvez également stocker la méthode XHR et l'abandonner lors d'événements de presse suivants afin de sauvegarder encore plus de bande passante. Voici quelque chose que j'ai écrit pour un de mes scripts autocomplete.

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 t'aides,

Marko

40
Marko
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/

16
azzy81

J'aime la réponse de Surreal Dream, mais j’ai trouvé que ma fonction "doneTyping" se déclenche à chaque pression de touche, c’est-à-dire si vous tapez "Hello" très rapidement; au lieu de ne déclencher qu'une seule fois lorsque vous arrêtez de taper, la fonction se déclenche 5 fois.

Le problème était que la fonction javascript setTimeout ne semble pas écraser ni tuer les anciens délais d'attente qui ont été définis, mais si vous le faites vous-même, cela fonctionne! Je viens donc d'ajouter un appel à clearTimeout juste avant setTimeout si typingTimer est défini. Voir ci-dessous:

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms, 5 second for example

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

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

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

N.B. J'aurais aimé simplement ajouter ceci en tant que commentaire à la réponse de Surreal Dream, mais je suis un nouvel utilisateur et je n'ai pas assez de réputation. Pardon!

10
Liam Flanagan

Modification de la réponse acceptée pour gérer des cas supplémentaires tels que le collage:

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

// updated events 
$input.on('input propertychange paste', function () {
    clearTimeout(typingTimer);
    typingTimer = setTimeout(doneTyping, doneTypingInterval);      
});

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

Les deux premières réponses ne fonctionnent pas pour moi. Alors, voici ma solution:

var timeout = null;

$('#myInput').keyup(function() {
    clearTimeout(timeout);

    timeout = setTimeout(function() {
        //do stuff here
    }, 500);
});
6
YSFKBDY

Je ne pense pas qu'un événement keyDown soit nécessaire dans ce cas (veuillez me dire pourquoi si je me trompe). Dans mon script (non-jquery), une solution similaire ressemble à ceci:

var _timer, _timeOut = 2000; 



function _onKeyUp(e) {
    clearTimeout(_timer);
    if (e.keyCode == 13) {      // close on ENTER key
        _onCloseClick();
    } else {                    // send xhr requests
        _timer = window.setTimeout(function() {
            _onInputChange();
        }, _timeOut)
    }

}

C'est ma première réponse sur Stack Overflow, j'espère donc que cela aidera quelqu'un, un jour :)

4
Rafal Pastuszak

Eh bien, à proprement parler non, car l'ordinateur ne peut pas deviner quand l'utilisateur a fini de taper. Vous pouvez bien sûr déclencher une minuterie sur la touche up et la réinitialiser à chaque touche suivante. Si la minuterie expire, l'utilisateur n'a pas tapé pour la durée de la minuterie - vous pouvez appeler cela "fini de taper".

Si vous attendez des utilisateurs qu'ils fassent des pauses lors de la frappe, il est impossible de savoir quand ils ont terminé.

(À moins bien sûr que vous puissiez dire à partir des données quand elles sont faites) 

4
sleske

J'ai l'impression que la solution est un peu plus simple avec l'événement input:

var typingTimer;
var doneTypingInterval = 500;

$("#myInput").on("input", function () {
    window.clearTimeout(typingTimer);
    typingTimer = window.setTimeout(doneTyping, doneTypingInterval);
});

function doneTyping () {
    // code here
}
2
Adrien Brunelat

SOLUTION:

Je mettais en œuvre la recherche sur ma liste et j'ai besoin qu'elle soit basée sur ajax. Cela signifie que, à chaque changement de clé, les résultats recherchés doivent être mis à jour et affichés. Cela fonctionne si bien que beaucoup d’appels ajax sont envoyés au serveur, ce qui n’est pas une bonne chose. Après quelques travaux, j’ai abordé le serveur ping lorsque le client cesse de taper.

La solution qui a fonctionné pour moi est la suivante:

$(document).ready(function() {

$('#yourtextfield').keyup(function() {
s = $('#yourtextfield').val();
setTimeout(function() { 
        if($('#yourtextfield').val() == s){ // Check the value searched is the latest one or not. This will help in making the ajax call work when client stops writing.
            $.ajax({
                type: "POST",
                url: "yoururl",
                data: 'search=' + s,
                cache: false,
                beforeSend: function() {
                   // loading image
                },
                success: function(data) {
                    // Your response will come here
                }
            })
        }
    }, 1000); // 1 sec delay to check.

    }); // End of  keyup function

    }); // End of document.ready

Vous avez remarqué qu’il n’est pas nécessaire d’utiliser un minuteur pour mettre cela en œuvre.

Je suis sûr que cela aidera les autres.

À

2
Ata ul Mustafa

Vous pouvez utiliser l'événement onblur pour détecter le moment où la zone de texte perd son focus: https://developer.mozilla.org/en/DOM/element.onblur

Ce n'est pas la même chose que "cesse de taper", si vous vous souciez du cas où l'utilisateur tape un tas de trucs et reste assis là avec la zone de texte toujours focalisée.

Pour cela, je suggèrerais de lier un setTimeout à l'événement onclick et de supposer qu'après x minutes sans frappes au clavier, l'utilisateur a cessé de taper.

1
Mike Ruhlin

Voici le code JS simple que j'ai écrit:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pt-br" lang="pt-br">
<head><title>Submit after typing finished</title>
<script language="javascript" type="text/javascript">
function DelayedSubmission() {
    var date = new Date();
    initial_time = date.getTime();
    if (typeof setInverval_Variable == 'undefined') {
            setInverval_Variable = setInterval(DelayedSubmission_Check, 50);
    } 
}
function DelayedSubmission_Check() {
    var date = new Date();
    check_time = date.getTime();
    var limit_ms=check_time-initial_time;
    if (limit_ms > 800) { //Change value in milliseconds
        alert("insert your function"); //Insert your function
        clearInterval(setInverval_Variable);
        delete setInverval_Variable;
    }
}

</script>
</head>
<body>

<input type="search" onkeyup="DelayedSubmission()" id="field_id" style="WIDTH: 100px; HEIGHT: 25px;" />

</body>
</html>
1
Alfredo Lingoist Jr.

Une fois que vous avez détecté le focus sur la zone de texte, effectuez une vérification de l'expiration du délai d'activation de la touche enfoncée et réinitialisez-la chaque fois qu'elle est déclenchée.

Lorsque le délai est écoulé, faites votre demande ajax.

0
ocodo

Je ne sais pas si mes besoins sont simplement bizarres, mais j'avais besoin de quelque chose de similaire et c'est ce que j'ai fini par utiliser:

$('input.update').bind('sync', function() {
    clearTimeout($(this).data('timer'));            
    $.post($(this).attr('data-url'), {value: $(this).val()}, function(x) {
        if(x.success != true) {
            triggerError(x.message);    
        }
    }, 'json');
}).keyup(function() {
    clearTimeout($(this).data('timer'));
    var val = $.trim($(this).val());
    if(val) {
        var $this = $(this);
        var timer = setTimeout(function() {
            $this.trigger('sync');
        }, 2000);
        $(this).data('timer', timer);
    }
}).blur(function() {
    clearTimeout($(this).data('timer'));     
    $(this).trigger('sync');
});

Ce qui me permet d'avoir des éléments comme celui-ci dans mon application:

<input type="text" data-url="/controller/action/" class="update">

Qui sont mis à jour lorsque l'utilisateur est "en train de taper" (pas d'action pendant 2 secondes) ou passe à un autre champ (estompe l'élément)

0
Paolo Bergantino

Plusieurs minuteries par page

Toutes les autres réponses ne fonctionnent que pour un contrôle ( mon autre réponse inclus). Si vous avez plusieurs contrôles par page (par exemple, dans un panier) seul le dernier contrôle où l'utilisateur a tapé quelque chose sera appelé . Dans mon cas, ce n'est certainement pas le comportement souhaité - chaque contrôle devrait avoir son propre minuteur.

Pour résoudre ce problème, vous devez simplement passer un identifiant à la fonction et gérer un dictionnaire timeoutHandles comme dans le code suivant:

Déclaration de fonction:

var delayUserInput = (function () {
    var timeoutHandles = {};    
    return function (id, callback, ms) {        
        if (timeoutHandles[id]) {
            clearTimeout(timeoutHandles[id]);
        }

        timeoutHandles[id] = setTimeout(callback, ms);             
    };
})();

Utilisation de la fonction:

  delayUserInput('yourID', function () {
     //do some stuff
  }, 1000);
0
Fabian Bigler

Pourquoi ne pas simplement utiliser onfocusout?

https://www.w3schools.com/jsreF/event_onfocusout.asp

S'il s'agit d'un formulaire, ils laisseront toujours le focus de chaque champ de saisie afin de cliquer sur le bouton d'envoi afin que vous sachiez qu'aucune entrée ne manquera de faire appeler son gestionnaire d'événements onfocusout. 

0
coffeeeee

Je viens de trouver un code simple pour attendre que l'utilisateur ait fini de taper:

step 1.set time out sur null, puis effacez le délai d'attente actuel lorsque l'utilisateur tape.

étape 2.trigger efface le délai d’attente de la variable définie avant le déclenchement de l'événement keyup.

étape 3.définir le délai d’attente à la variable déclarée ci-dessus;

<input type="text" id="input" placeholder="please type" style="padding-left:20px;"/>
<div class="data"></div>

code javascript

var textInput = document.getElementById('input');
var textdata = document.querySelector('.data');
// Init a timeout variable to be used below
var timefired = null;

// Listen for keystroke events
// Init a timeout variable to be used below
var timefired = null;// Listen for keystroke events
textInput.onkeyup = function (event) {
clearTimeout(timefired);
timefired = setTimeout(function () {
    textdata.innerHTML = 'Input Value:'+ textInput.value;
  }, 600);
};
0
Nevil Saul Blemuss

Si vous avez besoin d'attendre que l'utilisateur ait fini de taper, utilisez simplement ceci:

$(document).on('change','#PageSize', function () {
    //Do something after new value in #PageSize       
});

Exemple complet avec appel ajax - cela fonctionne pour mon téléavertisseur - nombre d’éléments par liste:

$(document).ready(function () {
    $(document).on('change','#PageSize', function (e) {
        e.preventDefault();
        var page = 1;
        var pagesize = $("#PageSize").val();
        var q = $("#q").val();
        $.ajax({
            url: '@Url.Action("IndexAjax", "Materials", new { Area = "TenantManage" })',
            data: { q: q, pagesize: pagesize, page: page },
            type: 'post',
            datatype: "json",
            success: function (data) {
                $('#tablecontainer').html(data);
               // toastr.success('Pager has been changed', "Success!");
            },
            error: function (jqXHR, exception) {
                ShowErrorMessage(jqXHR, exception);
            }
        });  
    });
});    
0
Miroslav Siska

Si vous recherchez une longueur spécifique (comme un champ de code postal):

$("input").live("keyup", function( event ){
if(this.value.length == this.getAttribute('maxlength')) {
        //make ajax request here after.
    }
  });
0
Jason

d'accord avec la réponse de @going. Une autre solution similaire qui a fonctionné pour moi est celle ci-dessous. La seule différence est que j'utilise .on ("entrée" ...) au lieu de keyup. Cela ne capture que les changements dans l’entrée. les autres touches comme Ctrl, Shift etc. sont ignorées

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

//on input change, start the countdown

$('#myInput').on("input", function() {    
    clearTimeout(typingTimer);
    typingTimer = setTimeout(function(){
        // doSomething...
    }, doneTypingInterval);
});
0
Grace.io

Déclarez la fonction delay suivante:

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

et ensuite l'utiliser:

let $filter = $('#item-filter');
$filter.on('keydown', function () {
    delay(function () {            
        console.log('this will hit, once user has not typed for 1 second');            
    }, 1000);
});    
0
Fabian Bigler