Come attivare un evento nel testo di input dopo aver smesso di digitare / scrivere?


85

Voglio attivare un evento subito dopo aver smesso di digitare (non durante la digitazione) caratteri nella casella di testo di input.

Ho provato con:

$('input#username').keypress(function() {
    var _this = $(this); // copy of this object for further usage

    setTimeout(function() {
        $.post('/ajax/fetch', {
            type: 'username',
            value: _this.val()
        }, function(data) {
            if(!data.success) {
                // continue working
            } else {
                // throw an error
            }
        }, 'json');
    }, 3000);
});

Ma questo esempio produce un timeout per ogni carattere digitato e ricevo circa 20 richieste AJAX se digito 20 caratteri.

Su questo violino mostro lo stesso problema con un semplice avviso invece di un AJAX.

C'è una soluzione per questo o sto solo usando un approccio sbagliato per questo?


1
Temo che javascript non fornisca un evento che ti consentirebbe di essere avvisato quando l'utente smette di digitare in un campo di input. Perché ne hai bisogno?
Darin Dimitrov

3
non è ovvio dall'esempio? Voglio attivare un evento "quando l'utente finale smette di digitarlo", invece di inviare 20 richieste

1
Non c'è modo di sapere quando l'utente ha effettivamente finito di digitare a meno che non invii o modifichi manualmente i campi. Come fai a sapere se l'utente fa una pausa a metà frase e aspetta 5 minuti prima di digitare di più? Una possibile soluzione sarebbe usare .blur () e inviare quando il focus degli utenti lascia il campo.
Kevin M

14
I commenti sopra sono sciocchi. Questo è un caso d'uso comune: voglio un evento quando l'utente ha finito di ridimensionare la finestra, zoomare la mappa, trascinare, digitare ... praticamente qualsiasi azione continua da parte dell'utente deve essere tradotta nel nostro universo digitale. Anche una singola sequenza di tasti soffre di questo problema: quando si preme un tasto, in realtà "rimbalza", creando non solo 1 evento di battitura, ma molti. L'hardware o il sistema operativo del tuo computer rimuove questi eventi extra, ed è per questo che abbiamo l'illusione di eventi di battitura discreti. Questo si chiama "debouncing", ed è ciò di cui ha bisogno l'OP.
Ziggy

avviso per gli utenti React
Maslow

Risposte:


172

Dovrai usare un setTimeout(come te) ma anche memorizzare il riferimento in modo da poter continuare a reimpostare il limite. Qualcosa di simile a:

//
// $('#element').donetyping(callback[, timeout=1000])
// Fires callback when a user has finished typing. This is determined by the time elapsed
// since the last keystroke and timeout parameter or the blur event--whichever comes first.
//   @callback: function to be called when even triggers
//   @timeout:  (default=1000) timeout, in ms, to to wait before triggering event if not
//              caused by blur.
// Requires jQuery 1.7+
//
;(function($){
    $.fn.extend({
        donetyping: function(callback,timeout){
            timeout = timeout || 1e3; // 1 second default timeout
            var timeoutReference,
                doneTyping = function(el){
                    if (!timeoutReference) return;
                    timeoutReference = null;
                    callback.call(el);
                };
            return this.each(function(i,el){
                var $el = $(el);
                // Chrome Fix (Use keyup over keypress to detect backspace)
                // thank you @palerdot
                $el.is(':input') && $el.on('keyup keypress paste',function(e){
                    // This catches the backspace button in chrome, but also prevents
                    // the event from triggering too preemptively. Without this line,
                    // using tab/shift+tab will make the focused element fire the callback.
                    if (e.type=='keyup' && e.keyCode!=8) return;
                    
                    // Check if timeout has been set. If it has, "reset" the clock and
                    // start over again.
                    if (timeoutReference) clearTimeout(timeoutReference);
                    timeoutReference = setTimeout(function(){
                        // if we made it here, our timeout has elapsed. Fire the
                        // callback
                        doneTyping(el);
                    }, timeout);
                }).on('blur',function(){
                    // If we can, fire the event since we're leaving the field
                    doneTyping(el);
                });
            });
        }
    });
})(jQuery);

$('#example').donetyping(function(){
  $('#example-output').text('Event last fired @ ' + (new Date().toUTCString()));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<input type="text" id="example" />
<p id="example-output">Nothing yet</p>

Ciò verrà eseguito quando:

  1. Il timeout è scaduto o
  2. L'utente ha cambiato campo ( blurevento)

(Qualunque cosa si verifichi per prima)


1
Sì, questo mi salva la notte. Grazie
novizio

2
@Brad: questa soluzione jquery funziona bene tranne per il fatto che il tasto backspace digitato nella casella di input non viene rilevato nell'ultimo chrome (38.0.2125.111). Solo cambiandolo per keyupfunzionare. Potresti voler ispezionare questo e modificare il codice in modo appropriato.
Palerdot

1
@palerdot: Buono a sapersi, grazie. Lo esaminerò e posterò eventuali modifiche.
Brad Christie

3
Grazie per questo plugin: funziona alla grande. Ho dovuto fare un aggiustamento, perché non funzionava quando qualcuno ha incollato qualcosa nel campo di input. Ho modificato la riga seguente per includere paste -$el.is(':input') && $el.on('keyup keypress paste',function(e){
Matt

1
@ Sangar82: Dovrebbe ora (anche se sono un po 'confuso, ctrl + v dovrebbe essere catturato premendo un tasto - a meno che tu non faccia clic con il pulsante destro del mouse-> incolla?
Brad Christie

71

SOLUZIONE:

Ecco la soluzione. Esecuzione di una funzione dopo che l'utente ha smesso di digitare per un periodo di tempo specificato:

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

Utilizzo

$('input').keyup(function() {
  delay(function(){
    alert('Hi, func called');
  }, 1000 );
});

1
user1386320 - Controlla questa soluzione Ho un problema simile e l'ho fatto funzionare. Aggiungerai il tuo codice al posto di alert ().
Ata ul Mustafa

Grazie. Funziona bene.
user1919

2
Posso aggiungere voti N + positivi? Questa soluzione è brillante, breve ed elegante utilizzando le chiusure JavaScript.
Matteo Conta

1
@MatteoConta, grazie :)
Ata ul Mustafa

1
Non volevo includere underscore.js nella mia pagina solo per questo. La tua soluzione funziona perfettamente! Grazie
Skoempie

17

Puoi utilizzare underscore.js "debounce"

$('input#username').keypress( _.debounce( function(){<your ajax call here>}, 500 ) );

Ciò significa che la chiamata alla funzione verrà eseguita dopo 500 ms dalla pressione di un tasto. Ma se si preme un altro tasto (viene attivato un altro evento di pressione di un tasto) prima dei 500 ms, la precedente esecuzione della funzione verrà ignorata (rimbalzata) e la nuova verrà eseguita dopo un nuovo timer di 500 ms.

Per ulteriori informazioni, l'utilizzo di _.debounce (func, timer, true ) significherebbe che la prima funzione verrà eseguita e tutti gli altri eventi di pressione dei tasti entro i successivi timer di 500 ms verranno ignorati.


Molto utile e breve se hai già sottolineato come lib.
Francesco Pasa


6

soluzione pulita:

$.fn.donetyping = function(callback, delay){
  delay || (delay = 1000);
  var timeoutReference;
  var doneTyping = function(elt){
    if (!timeoutReference) return;
    timeoutReference = null;
    callback(elt);
  };

  this.each(function(){
    var self = $(this);
    self.on('keyup',function(){
      if(timeoutReference) clearTimeout(timeoutReference);
      timeoutReference = setTimeout(function(){
        doneTyping(self);
      }, delay);
    }).on('blur',function(){
      doneTyping(self);
    });
  });

  return this;
};

6

È necessario assegnare setTimeoutuna variabile e utilizzare clearTimeoutper cancellarla premendo un tasto.

var timer = '';

$('input#username').keypress(function() {
  clearTimeout(timer);
  timer = setTimeout(function() {
    //Your code here
  }, 3000); //Waits for 3 seconds after last keypress to execute the above lines of code
});

Violino

Spero che sia di aiuto.


3

C'è qualche semplice plugin che ho creato che fa esattamente questo. Richiede molto meno codice rispetto alle soluzioni proposte ed è molto leggero (~ 0,6kb)

Per prima cosa crei un Bidoggetto che può essere in bumpedqualsiasi momento. Ogni urto ritarderà l' attivazione della richiamata di offerta per un determinato ammontare di tempo.

var searchBid = new Bid(function(inputValue){
    //your action when user will stop writing for 200ms. 
    yourSpecialAction(inputValue);
}, 200); //we set delay time of every bump to 200ms

Quando l' Bidoggetto è pronto, ne abbiamo bisogno in bumpqualche modo. Alleghiamo il bumping a keyup event.

$("input").keyup(function(){
    searchBid.bump( $(this).val() ); //parameters passed to bump will be accessable in Bid callback
});

Quello che succede qui è:

Ogni volta che l'utente preme il tasto, l'offerta viene "ritardata" (urtata) per i successivi 200 ms. Se passano 200 ms senza essere nuovamente "urtati", la richiamata verrà attivata.

Inoltre, hai 2 funzioni aggiuntive per interrompere l'offerta (se l'utente ha premuto esc o ha fatto clic all'esterno dell'input, ad esempio) e per terminare e attivare immediatamente la richiamata (ad esempio quando l'utente preme il tasto Invio):

searchBid.stop();
searchBid.finish(valueToPass);

1

Ho cercato un semplice codice HTML / JS e non ne ho trovato nessuno. Quindi, ho scritto il codice qui sotto usando onkeyup="DelayedSubmission()".

<!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>

0

perché fare così tanto quando vuoi solo resettare un orologio?

var clockResetIndex = 0 ;
// this is the input we are tracking
var tarGetInput = $('input#username');

tarGetInput.on( 'keyup keypress paste' , ()=>{
    // reset any privious clock:
    if (clockResetIndex !== 0) clearTimeout(clockResetIndex);

    // set a new clock ( timeout )
    clockResetIndex = setTimeout(() => {
        // your code goes here :
        console.log( new Date() , tarGetInput.val())
    }, 1000);
});

se stai lavorando su wordpress, devi avvolgere tutto questo codice all'interno di un blocco jQuery:

jQuery(document).ready(($) => {
    /**
     * @name 'navSearch' 
     * @version 1.0
     * Created on: 2018-08-28 17:59:31
     * GMT+0530 (India Standard Time)
     * @author : ...
     * @description ....
     */
        var clockResetIndex = 0 ;
        // this is the input we are tracking
        var tarGetInput = $('input#username');

        tarGetInput.on( 'keyup keypress paste' , ()=>{
            // reset any privious clock:
            if (clockResetIndex !== 0) clearTimeout(clockResetIndex);

            // set a new clock ( timeout )
            clockResetIndex = setTimeout(() => {
                // your code goes here :
                console.log( new Date() , tarGetInput.val())
            }, 1000);
        });
});

se desideri estendere jquery e desideri utilizzare questo metodo in più elementi di input, la risposta approvata è la risposta che stai cercando.
insCode


-1

Secondo me un utente smette di scrivere quando non si concentra su quell'input. Per questo hai una funzione chiamata "blur" che fa cose simili


4
sbagliato! sfocatura è un evento quando si perde il focus su un particolare campo di input. ciò di cui ho bisogno è determinare quando la tastiera non è in uso in quel campo in cui sto cercando di attivare un evento.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.