Detecte todas as alterações em um <input type = "text"> (imediatamente) usando JQuery

397

Há muitas maneiras pelas quais o valor de um <input type="text">pode mudar, incluindo:

  • pressionamentos de tecla
  • copiar colar
  • modificado com JavaScript
  • preenchido automaticamente pelo navegador ou uma barra de ferramentas

Quero que minha função JavaScript seja chamada (com o valor de entrada atual) sempre que for alterada. E quero que seja chamado imediatamente, não apenas quando a entrada perder o foco.

Estou procurando a maneira mais limpa e robusta de fazer isso em todos os navegadores (usando o jQuery de preferência).

Dustin Boswell
fonte

Respostas:

352

Esse código jQuery captura alterações imediatas em qualquer elemento e deve funcionar em todos os navegadores:

 $('.myElements').each(function() {
   var elem = $(this);

   // Save current value of element
   elem.data('oldVal', elem.val());

   // Look for changes in the value
   elem.bind("propertychange change click keyup input paste", function(event){
      // If value has changed...
      if (elem.data('oldVal') != elem.val()) {
       // Updated stored value
       elem.data('oldVal', elem.val());

       // Do action
       ....
     }
   });
 });
phatmann
fonte
19
Para referência de todos, inputé o evento HTML5 que, acredito, deve abranger todos os navegadores modernos ( referência MDN ). keyupdeve pegar o resto, embora com um pequeno atraso ( inputé acionado ao pressionar a tecla). propertychangeé um evento proprietário da MS e não tenho certeza se pasteé realmente necessário.
Jo # Liss
71
Estou errado ou isso não lidar quando o texto alterado através de javascript comodocument.getElementById('txtInput').value = 'some text';
Mehmet ATAS
5
Mehmet, o código não pretende capturar valores alterados via JS.
Phatmann
12
@ MehmetAtaş está parcialmente correto. A referência para o inputevento aqui afirma que ele não é acionado quando o conteúdo é modificado via JavaScript. No entanto, o onpropertychangeevento faz fogo sobre a modificação via JS (ver aqui ). Portanto, esse código funcionará quando o conteúdo for modificado via JS no IE, mas não funcionará em outros navegadores.
Vicky Chijwani
2
Você pode acionar um evento personalizado na entrada quando a propriedade muda, keyup etc. e, em seguida, pode ser usado em qualquer lugar.
Ivan Ivković
122

Uma solução sofisticada em tempo real para jQuery> = 1.9

$("#input-id").on("change keyup paste", function(){
    dosomething();
})

se você também deseja detectar o evento "clique", basta:

$("#input-id").on("change keyup paste click", function(){
    dosomething();
})

se você estiver usando jQuery <= 1.4, basta usar em livevez de on.

Felix
fonte
3
Isso tem uma desvantagem. Se você deixar o campo de entrada com a tecla tab, será acionado o evento keyup, mesmo que o conteúdo não tenha sido editado.
Pawka
2
Como detectar quando datetimepicker preenche o # input-id? Nenhum dos eventos (alterar, colar) funciona.
30515 Pathros
Isso é ótimo! Eu tentei com o jquery 1.8.3 e funciona. Eu ainda tenho algum código livequery () que tenho que substituir, portanto não consigo atualizar para o 1.9.1. Eu sei que é velho, mas o site inteiro também.
Asle
FYI: Esses eventos serão acionados com um valor em branco se o teclado for usado para selecionar uma data que não é válida. ie Fevereiro 30. stackoverflow.com/questions/48564568/...
Olmstov
107

Infelizmente, acho que setIntervalganha o prêmio:

<input type=text id=input_id />
<script>
setInterval(function() { ObserveInputValue($('#input_id').val()); }, 100);
</script>

É a solução mais limpa, com apenas 1 linha de código. Também é o mais robusto, já que você não precisa se preocupar com todos os diferentes eventos / maneiras pelas quais uma pessoa inputpode obter um valor.

As desvantagens de usar 'setInterval' não parecem se aplicar neste caso:

  • A latência de 100ms? Para muitas aplicações, 100ms é rápido o suficiente.
  • Adicionado carregamento no navegador? Em geral, adicionar muitos setIntervals pesados ​​na sua página é ruim. Mas, nesse caso específico, o carregamento da página adicionado é indetectável.
  • Não é escalável para muitas entradas? A maioria das páginas não possui mais do que um punhado de entradas, que você pode farejar no mesmo setInterval.
Dustin Boswell
fonte
21
Essa parece ser a única maneira de detectar alterações em um campo de entrada de um formulário ao usar o navegador Nintendo 3DS (Versão / 1.7552.EU).
31413 Johan
4
Nota: A sobrecarga pode ser facilitada. Se você começar setInterval quando a entrada recebe foco e clearInterval quando a entrada perdeu o foco
Tebe
10
Por que você criaria um intervalo de 100ms em execução constante, onde não há razão para estar em execução constantemente? EVENTOS pessoas. Usa-os.
Gavin
15
@Gavin, neste caso em particular, o JS não possui eventos. Encontre-nos um conjunto de eventos que possam reconhecer, entradas do usuário, pastas do usuário, cortes do usuário, inserções de javascript, remoções de javascript, preenchimentos automáticos de formulários, formulários ocultos, não exibidos formulários.
Naramsim
2
O @ Gavin JS não parece ter um evento alterado que é acionado TODAS AS HORAS, uma entrada é alterada. (basta fazer um document.getElementById (nome) .value = Math.random ()) para ver nenhuma das soluções de acionador de eventos funcionar.
Joeri
58

A ligação ao oninputevento parece funcionar bem na maioria dos navegadores sãos. O IE9 também suporta, mas a implementação é incorreta (o evento não é acionado ao excluir caracteres).

Com o jQuery versão 1.7+, o onmétodo é útil para vincular ao evento como este:

$(".inputElement").on("input", null, null, callbackFunction);
HRJ
fonte
12
oninputevento não funciona com input[type=reset]ou javascript edição como você pode ver neste teste: codepen.io/yukulele/pen/xtEpb
Yukulélé
2
@ Yukulélé, Pelo menos, podemos contornar isso mais facilmente do que tentar detectar todas as ações possíveis do usuário .
Pacerier
30

Resposta de 2017 : o evento de entrada faz exatamente isso para algo mais recente que o IE8.

$(el).on('input', callback)
Chris F Carroll
fonte
6
Desculpe, mas ... como isso é diferente da resposta de 2012 do HRJ acima? De qualquer forma, o inputevento está falhando ao detectar alterações no JS.
David
16

Infelizmente, não há evento ou conjunto de eventos que atenda aos seus critérios. As teclas pressionadas e copiar / colar podem ser tratadas com o keyupevento. Mudanças no JS são mais complicadas. Se você tem controle sobre o código que define a caixa de texto, sua melhor aposta é modificá-lo para chamar sua função diretamente ou acionar um evento do usuário na caixa de texto:

// Compare the textbox's current and last value.  Report a change to the console.
function watchTextbox() {
  var txtInput = $('#txtInput');
  var lastValue = txtInput.data('lastValue');
  var currentValue = txtInput.val();
  if (lastValue != currentValue) {
    console.log('Value changed from ' + lastValue + ' to ' + currentValue);
    txtInput.data('lastValue', currentValue);
  }
}

// Record the initial value of the textbox.
$('#txtInput').data('lastValue', $('#txtInput').val());

// Bind to the keypress and user-defined set event.
$('#txtInput').bind('keypress set', null, watchTextbox);

// Example of JS code triggering the user event
$('#btnSetText').click(function (ev) {
  $('#txtInput').val('abc def').trigger('set');
});

Se você não tiver controle sobre esse código, poderá usar setInterval()para 'observar' a caixa de texto para alterações:

// Check the textbox every 100 milliseconds.  This seems to be pretty responsive.
setInterval(watchTextbox, 100);

Esse tipo de monitoramento ativo não captura as atualizações 'imediatamente', mas parece ser rápido o suficiente para que não haja atraso perceptível. Como DrLouie apontou nos comentários, essa solução provavelmente não se ajusta bem se você precisar observar muitas entradas. Você sempre pode ajustar o 2º parâmetro setInterval()para verificar com mais ou menos frequência.

Annabelle
fonte
Para sua informação, na verdade, existem vários eventos que podem ser combinados para corresponder aos critérios do OP em vários graus nos navegadores (consulte os links no comentário da primeira pergunta). Essa resposta não usa qualquer dos referidos eventos, e, ironicamente, provavelmente vai funcionar muito bem em todos os navegadores, embora com um ligeiro atraso;)
Crescent Fresh
O OP deseja capturar alterações na caixa de texto via JavaScript (por exemplo, myTextBox.value = 'foo';nenhum evento JavaScript lida com essa situação.
Annabelle
11
E se alguém tivesse 50 campos para acompanhar?
DoctorLouie 22/12/2009
11
Sim, só tenho 1 campo no meu caso, mas não vejo por que isso não funcionaria razoavelmente bem em vários campos. Provavelmente seria mais eficiente ter apenas 1 setTimeout que verifica todas as entradas.
Dustin Boswell
11
@Douglas: Nenhum evento JavaScript lida com essa situação - Correção: o IE pode lidar com isso input.onpropertychangee o FF2 +, Opera 9.5, Safari 3 e Chrome 1 podem lidar com isso input.__defineSetter__('value', ...)( com o qual, embora esteja documentado como não funcional nos nós do DOM, posso verificar funciona). A única divergência em relação à implementação do IE é que o IE aciona o manipulador não apenas na configuração programática, mas também durante os principais eventos.
Crescent Fresh
6

Aqui está uma solução ligeiramente diferente se você não gostou de nenhuma das outras respostas:

var field_selectors = ["#a", "#b"];
setInterval(function() { 
  $.each(field_selectors, function() { 
    var input = $(this);
    var old = input.attr("data-old-value");
    var current = input.val();
    if (old !== current) { 
      if (typeof old != 'undefined') { 
        ... your code ...
      }
      input.attr("data-old-value", current);
    }   
  }   
}, 500);

Considere que você não pode confiar em cliques e teclas para capturar a colagem do menu de contexto.

Peder
fonte
Essa resposta funcionou melhor para mim. Eu estava tendo alguns problemas com meus seletores até que eu usei: $("input[id^=Units_]").each(function() {
robr
4

Adicione esse código em algum lugar, isso fará o truque.

var originalVal = $.fn.val;
$.fn.val = function(){
    var result =originalVal.apply(this,arguments);
    if(arguments.length>0)
        $(this).change(); // OR with custom event $(this).trigger('value-changed');
    return result;
};

A solução encontrada em val () não aciona change () no jQuery

lpradhap
fonte
3

Eu criei uma amostra. Que funcione para você.

var typingTimer;
var doneTypingInterval = 10;
var finaldoneTypingInterval = 500;

var oldData = $("p.content").html();
$('#tyingBox').keydown(function () {
    clearTimeout(typingTimer);
    if ($('#tyingBox').val) {
        typingTimer = setTimeout(function () {
            $("p.content").html('Typing...');
        }, doneTypingInterval);
    }
});

$('#tyingBox').keyup(function () {
    clearTimeout(typingTimer);
    typingTimer = setTimeout(function () {
        $("p.content").html(oldData);
    }, finaldoneTypingInterval);
});


<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>


<textarea id="tyingBox" tabindex="1" placeholder="Enter Message"></textarea>
<p class="content">Text will be replace here and after Stop typing it will get back</p>

http://jsfiddle.net/utbh575s/

Ambuj Khanna
fonte
3

Na verdade, não precisamos configurar loops para detectar alterações em javaScript. Já configuramos muitos ouvintes de eventos para o elemento que queremos detectar. apenas desencadear qualquer evento não prejudicial fará o trabalho.

$("input[name='test-element']").on("propertychange change click keyup input paste blur", function(){
console.log("yeh thats worked!");
});

$("input[name='test-element']").val("test").trigger("blur");

e ofc isso só estará disponível se você tiver o controle total das alterações de javascript no seu projeto.

Serdar
fonte
2

Bem, a melhor maneira é cobrir essas três bases que você listou por si mesmo. Um simples: onblur,: onkeyup, etc não funcionará para o que você deseja, então apenas combine-os.

O KeyUp deve abranger os dois primeiros e, se o Javascript estiver modificando a caixa de entrada, espero que seja o seu próprio javascript, portanto, basta adicionar um retorno de chamada na função que o modifica.

idrumgood
fonte
11
+1 para a solução simples, embora seja possível que o OP não possa / não queira modificar o código fazendo alterações na caixa de texto.
Annabelle
Enumerar todos os eventos possíveis não me parece robusto. O keyup funciona se o usuário mantém a tecla Delete pressionada (ou seja, uma tecla de repetição)? E o preenchimento automático (ou barras de ferramentas que preenchem valores automaticamente)? Ou existem fontes de entrada especiais que não acionam as teclas pressionadas? Eu ficaria preocupado que houvesse algo assim que você sentiria falta.
Dustin Boswell
1

Aqui está um exemplo de trabalho que estou usando para implementar uma variação de preenchimento automático e preenche um seletor jqueryui (lista), mas não quero que ele funcione exatamente como o preenchimento automático jqueryui que faz um menu suspenso.

$("#tagFilter").on("change keyup paste", function() {
     var filterText = $("#tagFilter").val();
    $("#tags").empty();
    $.getJSON("http://localhost/cgi-bin/tags.php?term=" + filterText,
        function(data) {
            var i;
            for (i = 0; i < data.length; i++) {
                var tag = data[i].value;
                $("#tags").append("<li class=\"tag\">" + tag + "</li>");
            }
        }); 
});
luz clara
fonte
1

NO JQUERY! (É meio obsoleto nos dias de hoje)

Editar: eu removi os eventos HTML. NÃO use eventos HTML ... use ouvintes de eventos Javascript.

document.querySelector("#testInput").addEventListener("input", test);

function test(e) {
 var a = document.getElementById('output');
 var b = /^[ -~]+$/;
 if (e.target.value == '' || b.test(e.target.value)) {
  a.innerText = "Text is Ascii?.. Yes";
 } else if (!b.test(e.target.value)) {
  a.innerText = "Text is Ascii?.. No";
 }
}
Try pressing Alt+NumPad2+NumPad3 in the input, it will instantly detect a change, whether you Paste, Type, or any other means, rather than waiting for you to unselect the textbox for changes to detect.

<input id="testInput">
<br>
<a id="output">Text is Ascii?.. Yes</a>

Mister SirCode
fonte
0

Você não pode simplesmente usar o <span contenteditable="true" spellcheck="false">elemento no lugar de <input type="text">?

<span>(com contenteditable="true" spellcheck="false"como atributos) distingue-se <input>principalmente por:

  • Não tem o estilo de um <input>.
  • Ele não possui uma valuepropriedade, mas o texto é renderizado como innerTexte faz parte de seu corpo interno.
  • É multilinha, <input>embora não seja, embora você defina o atributo multiline="true".

Para obter a aparência, é claro, você pode estilizá-la em CSS, enquanto escreve o valor que innerTextvocê pode obter para um evento:

Aqui está um violino .

Infelizmente, há algo que realmente não funciona no IE e no Edge, que não consigo encontrar.

Davide Cannizzo
fonte
Eu acho que existem preocupações de acessibilidade em torno do uso desse método.
Daniel Daniel Tonon às
0

Embora essa pergunta tenha sido publicada há 10 anos, acredito que ainda precise de algumas melhorias. Então aqui está a minha solução.

$(document).on('propertychange change click keyup input paste', 'selector', function (e) {
    // Do something here
});

O único problema com esta solução é que ela não será acionada se o valor mudar de como o javascript $('selector').val('some value'). Você pode disparar qualquer evento para o seu seletor quando alterar o valor do javascript.

$(selector).val('some value');
// fire event
$(selector).trigger('change');
HaxxanRaxa
fonte
-2

você pode ver este exemplo e escolher quais são os eventos que lhe interessam:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> 
<title>evetns</title>
</head>
<body>
<form>
    <input class="controlevents" id="i1" type="text" /><br />
    <input class="controlevents" id="i2" type="text" /><br />
    <input class="controlevents" id="i3" type="text" /><br />
    <input class="controlevents" id="i4" type="text" /><br />
    <input class="controlevents" id="i5" type="text" /><br />
</form>
<div id="datatext"></div>
</body>
</html>
<script>
$(function(){

function testingevent(ev){
    if (ev.currentTarget.tagName=="INPUT")
        $("#datatext").append("<div>id : " + ev.currentTarget.id + ", tag: " + ev.currentTarget.tagName + ", type: "+ ev.type +"</div>");
}   

    var eventlist = ["resizeend","rowenter","dragleave","beforepaste","dragover","beforecopy","page","beforeactivate","beforeeditfocus","controlselect","blur",
                    "beforedeactivate","keydown","dragstart","scroll","propertychange","dragenter","rowsinserted","mouseup","contextmenu","beforeupdate",
                    "readystatechange","mouseenter","resize","copy","selectstart","move","dragend","rowexit","activate","focus","focusin","mouseover","cut",
                    "mousemove","focusout","filterchange","drop","blclick","rowsdelete","keypress","losecapture","deactivate","datasetchanged","dataavailable",
                    "afterupdate","mousewheel","keyup","movestart","mouseout","moveend","cellchange","layoutcomplete","help","errorupdate","mousedown","paste",
                    "mouseleave","click","drag","resizestart","datasetcomplete","beforecut","change","error","abort","load","select"];

    var inputs = $(".controlevents");

    $.each(eventlist, function(i, el){
        inputs.bind(el, testingevent);
    });

});
</script>
andres descalzo
fonte
4
Eu acho que você esqueceu um tipo de evento :)
Dustin Boswell
-3

Talvez eu esteja atrasado para a festa aqui, mas você não pode apenas usar o evento .change () fornecido pelo jQuery.

Você deve ser capaz de fazer algo como ...

$(#CONTROLID).change(function(){
    do your stuff here ...
});

Você sempre pode vinculá-lo a uma lista de controles com algo como ...

var flds = $("input, textarea", window.document);

flds.live('change keyup', function() {
    do your code here ...
});

O fichário ativo garante que todos os elementos que existem na página agora e no futuro sejam tratados.

Andy Crouch
fonte
2
O evento de alteração é acionado somente após a perda do foco, portanto, não é acionado enquanto um usuário está digitando na caixa, somente depois que eles terminam e fazem outra coisa.
Eli Sand
2
Como muitas outras respostas aqui, isso não funcionará quando o valor do elemento for modificado do Javascript. Não sei se faltam outros casos de modificação solicitados pelo OP.
Mark Amery
-4

Esta é a maneira mais rápida e limpa de fazer isso:

Estou usando o Jquery ->

$('selector').on('change', function () {
    console.log(this.id+": "+ this.value);
});

Está funcionando muito bem para mim.

Despertaweb
fonte
7
Isso já foi sugerido. Ele aborda apenas casos simples, quando o usuário digita o texto.
Christophe