¿Cómo desencadenar un evento en el texto de entrada después de dejar de escribir / escribir?

84

Quiero activar un evento justo después de dejar de escribir (no mientras escribo) caracteres en mi cuadro de texto de entrada.

Lo he probado 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);
});

Pero este ejemplo produce un tiempo de espera para cada carácter escrito y obtengo alrededor de 20 solicitudes AJAX si escribo 20 caracteres.

En este violín demuestro el mismo problema con una alerta simple en lugar de un AJAX.

¿Existe una solución para esto o simplemente estoy usando un mal enfoque para esto?

Fuhrmanator
fuente
1
Me temo que javascript no proporciona un evento que le permita ser notificado cuando el usuario deje de escribir en un campo de entrada. ¿Por qué necesitas eso?
Darin Dimitrov
3
¿No es obvio por el ejemplo? Quiero activar un evento "cuando el usuario final deje de escribir", en lugar de enviar 20 solicitudes
1
No hay forma de saber cuándo el usuario ha terminado de escribir a menos que envíe o cambie los campos manualmente. ¿Cómo saber si el usuario hace una pausa a mitad de una frase y espera 5 minutos antes de escribir más? Una posible solución sería usar .blur () y enviar cuando el enfoque del usuario deja el campo.
Kevin M
14
Los comentarios anteriores son tontos. Este es un caso de uso común: quiero un evento cuando el usuario haya terminado de cambiar el tamaño de su ventana, hacer zoom en el mapa, arrastrar, escribir ... básicamente cualquier acción continua por parte del usuario debe traducirse en nuestro universo digital. Incluso una sola pulsación de tecla sufre este problema: cuando pulsa una tecla, en realidad "rebota", creando no solo una pulsación de tecla, sino muchas. El hardware o el sistema operativo de su computadora elimina estos eventos adicionales, y es por eso que tenemos la ilusión de eventos de pulsaciones de teclas discretas. Esto se llama "antirrebote", y esto es lo que necesita el OP.
Ziggy
advertencia para los usuarios de Reacción
Maslow

Respuestas:

172

Tendrá que usar un setTimeout(como usted) pero también almacenar la referencia para que pueda seguir restableciendo el límite. Algo como:

//
// $('#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>

Eso se ejecutará cuando:

  1. Ha transcurrido el tiempo de espera o
  2. El usuario cambió de campo ( blurevento)

(Lo que sea que venga primero)

Brad Christie
fuente
1
Sí, esto salva mi noche. Gracias
novato
2
@Brad: esta solución jquery funciona bien, excepto que la tecla de retroceso escrita en el cuadro de entrada no se detecta en la última versión de Chrome (38.0.2125.111). Solo cambiándolo a keyupfunciona. Es posible que desee inspeccionar esto y modificar el código de forma adecuada.
palerdot
1
@palerdot: Es bueno saberlo, gracias. Lo investigaré y publicaré los cambios.
Brad Christie
3
Gracias por este complemento, funciona muy bien. Tuve que hacer un ajuste, porque no funcionaría cuando alguien pegó algo en el campo de entrada. He editado la línea siguiente para incluir paste -$el.is(':input') && $el.on('keyup keypress paste',function(e){
Matt
1
@ Sangar82: Ahora debería (aunque estoy un poco confundido, ctrl + v debe ser capturado por pulsación de tecla - a menos que tengas razón-click-> pegar?
Brad Christie
71

SOLUCIÓN:

Aqui esta la solucion. Ejecutar una función después de que el usuario ha dejado de escribir durante un período de tiempo específico:

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

Uso

$('input').keyup(function() {
  delay(function(){
    alert('Hi, func called');
  }, 1000 );
});
Ata ul Mustafa
fuente
1
user1386320 - Verifique esta solución. Tengo un problema similar y lo hice funcionar. Agregará su código en lugar de alert ().
Ata ul Mustafa
Gracias. Funciona muy bien.
user1919
2
¿Puedo agregar N + Up votos? Esta solución es brillante, corta y elegante con cierres de JavaScript.
Matteo Conta
1
@MatteoConta, Gracias :)
Ata ul Mustafa
1
No quería incluir subrayado.js en mi página solo por esto. ¡Tu solución funciona perfectamente! Gracias
Skoempie
17

Puede usar underscore.js "debounce"

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

Esto significa que su llamada de función se ejecutará después de 500 ms de presionar una tecla. Pero si presiona otra tecla (se dispara otro evento de pulsación de tecla) antes de los 500 ms, la ejecución de la función anterior se ignorará (supresión de rebotes) y la nueva se ejecutará después de un nuevo temporizador de 500 ms.

Para obtener información adicional, usar _.debounce (func, timer, true ) significaría que la primera función se ejecutará y todos los demás eventos de pulsación de teclas dentro de los siguientes temporizadores de 500ms serían ignorados.

EscorpiónKing2k5
fuente
Muy útil y breve si ya lo ha subrayado como lib.
Francesco Pasa
10

¡Necesitas antirrebote!

Aquí hay un complemento de jQuery , y aquí está todo lo que necesita saber sobre debounce . Si viene aquí desde Google y Subrayado ha encontrado su camino en la JSoup de su aplicación, tiene antirrebote al horno justo en !

Ziggy
fuente
6

solución limpia:

$.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;
};
Matriz
fuente
5

Debe asignar setTimeoutuna variable y usarla clearTimeoutpara borrarla al presionar una tecla.

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

Violín

Espero que esto ayude.

Gibin Ealias
fuente
3

Hay un complemento simple que hice que hace exactamente eso. Requiere mucho menos código que las soluciones propuestas y es muy ligero (~ 0,6kb)

Primero crea un Bidobjeto que puede ser en bumpedcualquier momento. Cada golpe retrasará la activación de la devolución de llamada de oferta para la próxima cantidad de tiempo determinada.

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

Cuando el Bidobjeto está listo, lo necesitamos de bumpalguna manera. Adjuntemos golpes a keyup event.

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

Lo que pasa aquí es:

Cada vez que el usuario presiona la tecla, la oferta se 'retrasa' (golpea) durante los siguientes 200 ms. Si transcurren 200 ms sin que vuelva a sonar "golpeado", se activará la devolución de llamada.

Además, tiene 2 funciones adicionales para detener la oferta (si el usuario presionó esc o hizo clic en una entrada externa, por ejemplo) y para finalizar y activar la devolución de llamada inmediatamente (por ejemplo, cuando el usuario presiona la tecla Intro):

searchBid.stop();
searchBid.finish(valueToPass);
pie6k
fuente
1

He estado buscando un código HTML / JS simple y no encontré ninguno. Luego, escribí el siguiente código 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>
Alfredo Lingoist Jr.
fuente
0

¿Por qué hacer tanto cuando solo quieres reiniciar un reloj?

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

si está trabajando en wordpress, entonces necesita envolver todo este código dentro de un bloque 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);
        });
});
insCode
fuente
si desea extender jquery y desea usar este método en múltiples elementos de entrada, entonces la respuesta aprobada es la respuesta que está buscando.
insCode
0

Utilice el atributo onkeyup = "myFunction ()" en el <input>de su html.

HesamJafarian
fuente
-1

En mi opinión, un usuario deja de escribir cuando no se concentra en esa entrada. Para esto tienes una función llamada "desenfoque" que hace cosas como

Stefanz
fuente
4
¡incorrecto! el desenfoque es un evento en el que se pierde el foco en un campo de entrada en particular. lo que necesito es determinar cuándo el teclado no está en uso en ese campo en el que estoy tratando de activar un evento.