Jak wywołać zdarzenie w tekście wejściowym po tym, jak przestanę pisać / pisać?

85

Chcę wywołać zdarzenie zaraz po tym, jak przestanę wpisywać (nie podczas wpisywania) znaków w moim polu tekstowym.

Próbowałem z:

$('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);
});

Ale ten przykład generuje limit czasu dla każdego wpisanego znaku i otrzymuję około 20 żądań AJAX, jeśli wpiszę 20 znaków.

Na tych skrzypcach pokazuję ten sam problem za pomocą prostego alertu zamiast AJAX.

Czy jest na to rozwiązanie, czy po prostu używam złego podejścia?

Fuhrmanator
źródło
1
Obawiam się, że javascript nie zapewnia zdarzenia, które pozwoliłoby na otrzymanie powiadomienia, gdy użytkownik przestanie pisać w polu wejściowym. Dlaczego tego potrzebujesz?
Darin Dimitrov
3
czy nie jest to oczywiste z przykładu? Chcę wywołać zdarzenie „gdy użytkownik końcowy przestanie go wpisywać”, zamiast wysyłać 20 żądań
1
Nie ma sposobu, aby stwierdzić, kiedy użytkownik faktycznie skończył pisać, chyba że ręcznie wprowadzi lub zmieni pola. Skąd masz wiedzieć, czy użytkownik zatrzymuje się w połowie zdania i czeka 5 minut, zanim zacznie pisać więcej? Możliwym rozwiązaniem byłoby użycie .blur () i wysyłanie, gdy fokus użytkownika opuszcza pole.
Kevin M
14
Powyższe komentarze są głupie. Jest to typowy przypadek użycia: chcę jednego zdarzenia, gdy użytkownik skończy zmieniać rozmiar swojego okna, powiększać mapę, przeciągać, pisać ... zasadniczo każda ciągła akcja ze strony użytkownika musi zostać przetłumaczona na nasz cyfrowy wszechświat. Problem dotyczy nawet pojedynczego naciśnięcia klawisza: po uderzeniu w klawisz w rzeczywistości „odbija się”, tworząc nie tylko jedno naciśnięcie klawisza, ale wiele. Sprzęt lub system operacyjny komputera usuwa te dodatkowe zdarzenia i dlatego mamy iluzję dyskretnych zdarzeń naciśnięcia klawisza. Nazywa się to „odbijaniem” i tego właśnie potrzebuje PO.
Ziggy
ostrzeżenie dla użytkowników reagujących: stackoverflow.com/a/28046731/57883
Maslow

Odpowiedzi:

172

Będziesz musiał użyć setTimeout(tak jak jesteś), ale także zapisać odniesienie, aby móc resetować limit. Coś jak:

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

To się wykona, gdy:

  1. Upłynął limit czasu lub
  2. Użytkownik zmienił pola ( blurzdarzenie)

(Cokolwiek będzie pierwsze)

Brad Christie
źródło
1
Tak, to oszczędza mi noc. Dzięki
nowicjusz
2
@Brad: To rozwiązanie jquery działa dobrze, z wyjątkiem tego, że klawisz Backspace wpisany w polu wprowadzania nie jest wykrywany w najnowszym chrome (38.0.2125.111). Tylko zmiana tego na keyupdziała. Możesz chcieć to sprawdzić i odpowiednio zmodyfikować kod.
palerdot
1
@palerdot: Dobrze wiedzieć, dziękuję. Przyjrzę się temu i opublikuję wszelkie zmiany.
Brad Christie
3
Dzięki za tę wtyczkę - działa świetnie. Musiałem zrobić jedną korektę, ponieważ nie zadziała, gdy ktoś wklei coś w pole wejściowe. Zredagowałem następujący wiersz, aby uwzględnić paste -$el.is(':input') && $el.on('keyup keypress paste',function(e){
Matt
1
@ Sangar82: Czy teraz (chociaż jestem trochę zdezorientowany, ctrl + v powinno być przechwycone przez naciśnięcie klawisza - chyba że klikniesz prawym przyciskiem myszy -> wklejasz?
Brad Christie
71

ROZWIĄZANIE:

Oto rozwiązanie. Wykonywanie funkcji po tym, jak użytkownik przestał pisać przez określony czas:

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

Stosowanie

$('input').keyup(function() {
  delay(function(){
    alert('Hi, func called');
  }, 1000 );
});
Ata ul Mustafa
źródło
1
user1386320 - sprawdź to rozwiązanie Mam podobny problem i mam to działające. Kod dodasz w miejsce alertu ().
Ata ul Mustafa
Dzięki. Działa ładnie.
user1919
2
Czy mogę dodać głosy N + Up? To rozwiązanie jest genialne, krótkie i eleganckie przy użyciu zamknięć JavaScript.
Matteo Conta
1
@MatteoConta, dziękuję :)
Ata ul Mustafa
1
Nie chciałem dołączać podkreślenia.js do mojej strony tylko w tym celu. Twoje rozwiązanie działa idealnie! Dzięki
Skoempie
17

Możesz użyć underscore.js „debounce”

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

Oznacza to, że wywołanie funkcji zostanie wykonane po 500 ms od naciśnięcia klawisza. Ale jeśli naciśniesz inny klawisz (zostanie uruchomione kolejne zdarzenie naciśnięcia klawisza) przed 500 ms, poprzednie wykonanie funkcji zostanie zignorowane (usunięte), a nowa zostanie wykonana po nowym liczniku czasu 500 ms.

Aby uzyskać dodatkowe informacje, użycie _.debounce (func, timer, true ) oznaczałoby, że pierwsza funkcja zostanie wykonana, a wszystkie inne zdarzenia naciśnięcia klawisza z kolejnymi licznikami czasu 500 ms byłyby ignorowane.

ScorpionKing2k5
źródło
Bardzo przydatne i krótkie, jeśli masz już podkreślenie jako biblioteka.
Francesco Pasa,
10

Potrzebujesz odbicia!

Oto wtyczka jQuery i wszystko, co musisz wiedzieć o debounce . Jeżeli przyjeżdżasz tu z Google i podkreślenia znalazła swoją drogę do JSoup swojej aplikacji, ma ZWLOKA pieczone w prawo !

Ziggy
źródło
6

oczyszczony roztwór:

$.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;
};
Matryca
źródło
6

Powinieneś przypisać setTimeoutdo zmiennej i użyć, clearTimeoutaby wyczyścić ją po naciśnięciu klawisza.

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

Skrzypce

Mam nadzieję że to pomoże.

Gibin Ealias
źródło
3

Jest kilka prostych wtyczek , które stworzyłem, które dokładnie to robią. Wymaga znacznie mniej kodu niż proponowane rozwiązania i jest bardzo lekki (~ 0,6kb)

Najpierw tworzysz Bidobiekt niż bumpedkiedykolwiek. Każde uderzenie opóźni wywołanie zwrotnej oferty na następny podany czas.

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

Gdy Bidobiekt jest gotowy, bumpjakoś tego potrzebujemy . Dołączmy do tego wpadanie keyup event.

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

Oto co się tutaj dzieje:

Za każdym razem, gdy użytkownik naciśnie klawisz, oferta jest „opóźniana” (zderzana) o następne 200 ms. Jeśli minie 200 ms bez ponownego `` uderzenia '', zostanie wywołany callback.

Ponadto masz 2 dodatkowe funkcje do zatrzymywania oferty (na przykład, jeśli użytkownik naciśnie klawisz esc lub kliknie zewnętrzne wejście) oraz do natychmiastowego zakończenia i wywołania wywołania zwrotnego (na przykład, gdy użytkownik naciśnie klawisz Enter):

searchBid.stop();
searchBid.finish(valueToPass);
Adam Pietrasiak
źródło
1

Szukałem prostego kodu HTML / JS i nie znalazłem żadnego. Następnie napisałem poniższy kod za pomocą 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.
źródło
0

po co tak dużo robić, skoro po prostu chcesz zresetować zegar?

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

jeśli pracujesz na wordpressie, musisz zawinąć cały ten kod wewnątrz bloku 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
źródło
jeśli chcesz rozszerzyć jquery i chcesz użyć tej metody w wielu elementach wejściowych, to zatwierdzona odpowiedź jest odpowiedzią, której szukasz.
insCode
0

Użyj atrybutu onkeyup = "myFunction ()" w <input>swoim html.

HesamJafarian
źródło
-1

Moim zdaniem użytkownik przestaje pisać, gdy nie skupia się na tym, co wpisuje. Do tego masz funkcję o nazwie "blur", która robi takie rzeczy

stefanz
źródło
4
źle! rozmycie to zdarzenie, w którym następuje utrata ostrości na określonym polu wejściowym. muszę określić, kiedy klawiatura nie jest używana w tym polu, w którym próbuję wywołać zdarzenie.