Executar a função javascript quando o usuário terminar de digitar em vez de pressionar a tecla up?


463

Desejo acionar uma solicitação ajax quando o usuário terminar de digitar em uma caixa de texto. Eu não quero que ele execute a função toda vez que o usuário digita uma letra, porque isso resultaria em MUITAS solicitações de ajax, no entanto, não quero que eles precisem pressionar o botão Enter também.

Existe uma maneira de detectar quando o usuário terminou de digitar e depois fazer a solicitação ajax?

Usando jQuery aqui! Dave


21
Eu acho que você precisará definir "terminar de digitar" para nós.
Sonhos surreais

8
Embora a resposta do @Surreal Dreams atenda à maioria dos seus requisitos, se o usuário começar a digitar novamente APÓS o tempo limite especificado, várias solicitações serão enviadas ao servidor. Veja minha resposta abaixo, que armazena cada solicitação XHR em uma variável e a cancela antes de disparar uma nova. Na verdade, é isso que o Google faz na pesquisa instantânea .
Marko

1
possível duplicação do atraso da inicialização
CMS

1
E o borrão? Eu acho que o usuário definitivamente terminou de digitar quando o elemento de entrada perde o foco.
Don P

3
Uma simples pesquisa no Google poderia ter lhe dado a resposta simples: schier.co/blog/2014/12/08/…
Cannicide

Respostas:


689

Então, acho que terminar de digitar significa que você apenas pára por um tempo, digamos 5 segundos. Portanto, com isso em mente, vamos iniciar um timer quando o usuário soltar uma tecla e limpá-la quando pressionar uma. Decidi que a entrada em questão será #myInput.

Fazendo algumas suposições ...

//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
Obrigado :) Comecei a digitar meu comentário sobre a pergunta e percebi que tinha uma idéia decente.
Sonhos surreais

26
Esta resposta não funciona corretamente, sempre será acionada após 5 segundos, a menos que o usuário digite muito lentamente. Veja a solução de trabalho abaixo.
indo

3
Se você tiver problemas aqui, porque o timer é acionado imediatamente, tente adicionar aspas à chamada de função: setTimeout ('functionToBeCalled', doneTypingInterval);
Largo

3
Estou vendo alguns comentários em que a função de retorno de chamada, doneTypingno exemplo, está sendo executada imediatamente. Isso geralmente é resultado da inclusão acidental ()após o nome da função. Note que ele deve ler setTimeout(doneTyping,nãosetTimeout(doneTyping(),
Anthony DiSanti

2
@ Jessica para trabalhar com colar, você deve adicionar o evento 'paste' como este: on ('keyup paste',
Sergey

397

A resposta escolhida acima não funciona.

Como o TypingTimer é ocasionalmente definido várias vezes (a tecla pressionada duas vezes antes de a tecla pressionada ser acionada para digitadores rápidos etc.), ela não é limpa corretamente.

A solução abaixo resolve esse problema e chama X segundos após o término, conforme solicitado pelo OP. Também não requer mais a função redundante de pressionamento de tecla. Também adicionei uma verificação para que sua chamada de função não aconteça se sua entrada estiver vazia.

//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
}

E o mesmo código na solução 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
}

Esta solução usa o ES6, mas não é necessária aqui. Basta substituir letpor vare a função de seta por uma função regular.


5
Bem, pode ser útil ainda chamar a função, mesmo que a entrada esteja vazia. E se você quiser limpar alguns resultados da pesquisa (por exemplo) quando a entrada estiver em branco novamente?
rvighne

7
Acho que também devemos considerar a condição em que o usuário simplesmente cola a string dentro do campo.
Vishal Nair

10
use $('#myInput').on('input', function() {se você quiser que isso funcione com copiar e colar. Não vejo o ponto da verificação se a entrada está vazia. Supondo que ele queira apagar o que digitou, isso falhará na chamada ajax.
billynoah

3
Por que é em if ($('#myInput').val)vez de if ($('#myInput').val())? Deve .valser uma função?
Wlnirvana

4
Por que não usar $ (this) .val ()?
JoelFan

76

É apenas uma linha com a função de rebaixo underscore.js :

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

Isso basicamente diz: Algo 500 milissegundos depois que eu paro de digitar.

Para mais informações: http://underscorejs.org/#debounce


5
Também parece estar disponível para jQuery: code.google.com/p/jquery-debounce e github.com/diaspora/jquery-debounce
koppor

63
Surpreende-me como as pessoas consideram "UMA LINHA DE CÓDIGO" como sendo extremamente importantes. Ótimo. UMA LINHA de código que requer um arquivo JS de 1,1k para carregar. Não estou votando contra isso porque é uma solução. Mas a coisa ousada de uma linha me irrita e também leva ao código inchado.
Wolfie

1
@ Wolfie, eu concordo com você sobre o inchaço do código, mas o Underscore e o Lodash são os dois principais utilitários dependentes do NPM, portanto essa pode ser uma solução de uma linha para muitas pessoas.
Paul

3
@Paul Concordo que, se você estiver carregando as bibliotecas para outros fins, é bom e realmente benéfico usar código comum. Mas carregar um K de código com o objetivo de uma linha não é eficiente. Especialmente com as plataformas móveis se tornando cada vez menos dados ilimitados. E muitas áreas do euro também pagam pela internet pelos dados. Portanto, é bom prestar atenção ao inchaço quando for razoável fazê-lo.
Wolfie

por que eu tenho:Uncaught ReferenceError: _ is not defined
Wilf 15/11

44

Sim, você pode definir um tempo limite de, digamos, 2 segundos em cada evento de ativação de tecla que acionará uma solicitação ajax. Você também pode armazenar o método XHR e abortá-lo nos eventos de pressionamento de tecla subseqüentes, para economizar ainda mais a banda. Aqui está algo que eu escrevi para um script de preenchimento automático meu.

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

Espero que isto ajude,

Marko


Eu não recomendaria isso. O código aciona uma solicitação de API cada vez que uma tecla é pressionada. Em seguida, cancela a solicitação se outra tecla for pressionada. Mesmo que essa solução impeça que o retorno de chamada AJAX seja acionado enquanto o usuário digita, ele ainda martelará o servidor com solicitações.
Lxg 27/03

Ixg, não, isso não vai. A função clearTimeout que ele usa limpa os eventos anteriores de Timeout, invocando a chamada da API.
John Smith

@JohnSmith, eu discordo é provavelmente melhor para primeiro limpar o timer, em seguida, verificar se xhr está vazio ou qualquer que seja sua condição pode ser e, finalmente, executar o temporizador com a chamada ajax
Fleuv

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

exemplo completo aqui: http://jsfiddle.net/ZYXp4/8/


Isso funcionou para mim, embora tenha em mente que, quando o usuário sair da caixa de texto, você não receberá um evento de keyup, pois ele perdeu o foco nesse ponto. A configuração para keydown parece resolver o problema.
horatius83

12

As duas principais respostas não funcionam para mim. Então, aqui está a minha solução:

var timeout = null;

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

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

11

Gosto da resposta do Surreal Dream, mas descobri que minha função "doneTyping" seria acionada a cada pressionamento de tecla, ou seja, se você digitar "Hello" muito rapidamente; em vez de disparar apenas uma vez quando você para de digitar, a função dispara 5 vezes.

O problema era que a função setTimeout do javascript não parece substituir ou eliminar os tempos limite antigos que foram definidos, mas se você fizer isso sozinho, funcionará! Então, acabei de adicionar uma chamada clearTimeout imediatamente antes do setTimeout se o typTimer estiver definido. Ver abaixo:

//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
}

NB: Eu gostaria de ter adicionado isso como um comentário à resposta do Surreal Dream, mas sou um usuário novo e não tenho reputação suficiente. Desculpa!


10

Modificando a resposta aceita para lidar com casos adicionais, como colar:

//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
}

6

Eu não acho que o evento keyDown seja necessário neste caso (por favor, me diga por que se eu estiver errado). No meu script (não-jquery), solução semelhante é assim:

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

}

É a minha primeira resposta no Stack Overflow, então espero que isso ajude alguém, algum dia :)


6

Declare a seguinte delayfunção:

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

e depois use-o:

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

Sua solução é fantástica, mano!
Desenvolvedor

1
@ Desenvolvedor Muito obrigado. Também adicionei outra resposta para resolver um cenário um pouco mais complexo (vários controles por página).
Fabian Bigler

boa solução. Im usando este mas em vez de keydown im usando o 'input' evento
Budyn

Com o evento keyup, funcionou muito bem para mim. Obrigado.
Sinan Eldem

6

Resposta tardia, mas estou adicionando porque é 2019 e isso é totalmente possível usando o ES6, sem bibliotecas de terceiros, e acho que a maioria das respostas altamente classificadas é volumosa e sobrecarregada com muitas variáveis.

Solução elegante retirada deste excelente post no blog.

function debounce(callback, wait) {
  let timeout;
  return (...args) => {
      const context = this;
      clearTimeout(timeout);
      timeout = setTimeout(() => callback.apply(context, args), wait);
  };
}

window.addEventListener('keyup', debounce( () => {
    // code you would like to run 1000ms after the keyup event has stopped firing
    // further keyup events reset the timer, as expected
}, 1000))

1
Esta realmente deve ser a melhor resposta dada no ano
Dark Hippo

4

Bem, a rigor, não, pois o computador não consegue adivinhar quando o usuário terminou de digitar. Obviamente, você pode acionar um cronômetro ao pressionar a tecla e redefini-lo a cada tecla subsequente. Se o timer expirar, o usuário não digitou a duração do timer - você pode chamar isso de "digitação concluída".

Se você espera que os usuários façam pausas durante a digitação, não há como saber quando elas terminam.

(A não ser, é claro, que você saiba dos dados quando eles forem concluídos)


3

Eu sinto que a solução é um pouco mais simples com o inputevento:

var typingTimer;
var doneTypingInterval = 500;

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

function doneTyping () {
    // code here
}

3

concorde com a resposta do @ going. Outra solução semelhante que funcionou para mim é a abaixo. A única diferença é que estou usando .on ("input" ...) em vez de keyup. Isso captura apenas as alterações na entrada. outras teclas como Ctrl, Shift etc. são ignoradas

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

3

Acabei de descobrir um código simples para aguardar o usuário terminar de digitar:

etapa 1. defina o tempo limite como nulo e limpe o tempo limite atual quando o usuário estiver digitando.

passo 2. desencadeie o tempo limite para a variável definir antes que o evento de keyup seja acionado.

etapa 3.define o tempo limite para a variável declarada acima;

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

código 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);
};

1
a pergunta já foi respondida ... e é bastante semelhante a este
Mixone

3

Eu estava implementando a pesquisa na minha listagem e precisava que ela fosse baseada no ajax. Isso significa que, a cada mudança de chave, os resultados pesquisados ​​devem ser atualizados e exibidos. Isso resulta em muitas chamadas ajax enviadas ao servidor, o que não é uma coisa boa.

Depois de algum trabalho, fiz uma abordagem para executar ping no servidor quando o usuário para de digitar.

Esta solução funcionou para mim:

$(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

Você notará que não há necessidade de usar nenhum timer durante a implementação.


2

Este é o código JS simples que escrevi:

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

Você pode usar o evento onblur para detectar quando a caixa de texto perde o foco: https://developer.mozilla.org/en/DOM/element.onblur

Isso não é o mesmo que "para de digitar", se você se importa com o caso em que o usuário digita várias coisas e fica sentado com a caixa de texto ainda focada.

Para isso, sugiro vincular um setTimeout ao evento onclick e supor que após x quantidade de tempo sem pressionar as teclas, o usuário parou de digitar.


1

Por que não usar o onfocusout?

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

Se for um formulário, eles sempre deixarão o foco de todos os campos de entrada para clicar no botão enviar para que você saiba que nenhuma entrada perderá a chamada de seu manipulador de eventos onfocusout.


1
onfocusout teve pouco suporte quando a pergunta foi feita (quase 7 anos atrás agora), e também o efeito com onfocusout seria um pouco diferente. Você teria que esperar o usuário deixar o foco no elemento, enquanto que com a solução de tempo limite / rejeição "é acionada" assim que o usuário para de digitar e o usuário não precisa mudar de foco. Um exemplo de caso de uso seria um daqueles formulários de registro em que, quando o usuário para de inserir um nome de usuário em potencial, uma marca de seleção ou "X" aparece à direita do campo do formulário, indicando que o nome está disponível.
David Zorychta 29/08

Se a barra de pesquisa está em foco, ninguém precisa vir de fora, depois de algum resultado tempo será diretamente como não aberta é a digitação mais
P Satish Patro

1

Se for necessário que o usuário se afaste do campo, podemos usar "onBlur" em vez de Onchange em Javascript

  <TextField id="outlined-basic"  variant="outlined" defaultValue={CardValue} onBlur={cardTitleFn} />

Se isso não for necessário, definir o temporizador seria a boa opção.


0

Depois de detectar o foco na caixa de texto, na tecla para cima, faça uma verificação de tempo limite e redefina-a sempre que for acionada.

Quando o tempo limite terminar, faça sua solicitação de ajax.


0

Se você estiver procurando por um comprimento específico (como um campo de CEP):

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

Não é uma má idéia fazer validação limitada antes de enviar a solicitação ajax.
chim 12/10

0

Não tenho certeza se minhas necessidades são meio estranhas, mas eu precisava de algo semelhante a isso e foi isso que acabei usando:

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

O que me permite ter elementos como este no meu aplicativo:

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

Que são atualizados quando o usuário "termina de digitar" (sem ação por 2 segundos) ou passa para outro campo (desfoque o elemento)


0

Se você precisar esperar até que o usuário termine de digitar, use simples:

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

Exemplo completo com chamada ajax - funcionando para meu pager - contagem de itens por lista:

$(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

Vários temporizadores por página

Todas as outras respostas funcionam apenas para um controle ( minha outra resposta incluída). Se você tiver vários controles por página (por exemplo, em um carrinho de compras), apenas o último controle em que o usuário digitou algo será chamado . No meu caso, esse certamente não é o comportamento desejado - cada controle deve ter seu próprio cronômetro.

Para resolver isso, basta passar um ID para a função e manter um dicionário timeoutHandles como no código a seguir:

Declaração de Função:

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

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

Uso da função:

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

0

Simples e fácil de se entender.

var mySearchTimeout;
$('#ctl00_mainContent_CaseSearch').keyup(function () {
   clearTimeout(mySearchTimeout);
   var filter = $(this).val();
   mySearchTimeout = setTimeout(function () { myAjaxCall(filter); }, 700);
   return true;
});

0

Para passar parâmetros para sua função junto com a sintaxe ES6.

$(document).ready(() => {
    let timer = null;
     $('.classSelector').keydown(() => {
     clearTimeout(timer); 
     timer = setTimeout(() => foo('params'), 500);
  });
});

const foo = (params) => {
  console.log(`In foo ${params}`);
}

-2

Uau, até 3 comentários são bastante corretos!

  1. A entrada vazia não é um motivo para ignorar a chamada de função, por exemplo, removo o parâmetro de desperdício da url antes de redirecionar

  2. .on ('input', function() { ... });deve ser usado para disparar keyup, pastee changeeventos

  3. definitivamente .val()ou .valuedeve ser usado

  4. Você pode usar a $(this)função de evento interno em vez de #idtrabalhar com várias entradas

  5. (a minha decisão) Eu uso função anônima em vez de doneTypingem setTimeoutfacilmente acesso $(this)a partir n.4, mas você precisa salvá-lo primeiro comovar $currentInput = $(this);

EDIT Vejo que algumas pessoas não entendem instruções sem a possibilidade de copiar e colar o código pronto. Aqui você está

var typingTimer;
//                  2
$("#myinput").on('input', function () {
    //             4     3
    var input = $(this).val();
    clearTimeout(typingTimer);
    //                           5
    typingTimer = setTimeout(function() {
        // do something with input
        alert(input);
    }, 5000);      
});

Como isso responde à pergunta?
Thomas Orlita

@ThomasOrlita, esta resposta complementa as 3 respostas mais votadas. Dê uma olhada em um aceita: 1. ele não suporta paste, ele não usa this, etc. (eu acho que é importante, mas eu não quero ficar perdido em toneladas de comentários)
vladkras

1
Isso não responde à pergunta. Se você gostaria de comentar sobre a outra resposta, há um sistema de comentários (estou usando isso agora!)
GrayedFox
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.