Detecta todos los cambios en un <input type = "text"> (inmediatamente) usando JQuery

397

Hay muchas formas en que <input type="text">puede cambiar el valor de una lata, incluyendo:

  • pulsaciones de teclas
  • copiar pegar
  • modificado con JavaScript
  • completado automáticamente por el navegador o una barra de herramientas

Quiero que se llame a mi función JavaScript (con el valor de entrada actual) cada vez que cambie. Y quiero que se llame de inmediato, no solo cuando la entrada pierde el foco.

Estoy buscando la forma más limpia y robusta de hacer esto en todos los navegadores (usando jQuery preferiblemente).

Dustin Boswell
fuente

Respuestas:

352

Este código jQuery detecta cambios inmediatos en cualquier elemento y debería funcionar en todos los 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
fuente
19
Para referencia de todos, inputes el evento HTML5 que creo que debería cubrir todos los navegadores modernos ( referencia MDN ). keyupdebería atrapar el resto, aunque con un ligero retraso ( inputse activa al presionar la tecla). propertychangees un evento propietario de MS, y no estoy seguro de si pastees realmente necesario.
Jo Liss el
71
¿Me equivoco o esto no se maneja cuando el texto se cambia a través de JavaScript comodocument.getElementById('txtInput').value = 'some text';
Mehmet Ataş
55
Mehmet, el código no está destinado a capturar valores cambiados a través de JS.
phatmann
12
@ MehmetAtaş es en parte correcto. La referencia para el inputevento aquí indica que no se dispara cuando los contenidos se modifican a través de JavaScript. Sin embargo, el onpropertychangeevento se dispara en la modificación a través de JS (ver aquí ). Por lo tanto, este código funcionará cuando los contenidos se modifiquen a través de JS en IE, pero no funcionará en otros navegadores.
Vicky Chijwani
2
Puede desencadenar un evento personalizado en la entrada cuando el cambio de propiedad, keyup, etc., y luego usarlo en cualquier lugar.
Ivan Ivković
122

Una solución elegante en tiempo real para jQuery> = 1.9

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

si también desea detectar el evento "clic", simplemente:

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

si está usando jQuery <= 1.4, simplemente use en livelugar de on.

Felix
fuente
3
Esto tiene un inconveniente. Si deja el campo de entrada con la tecla de tabulación, arrojará un evento de keyup incluso si el contenido no se editó.
Pawka
2
¿Cómo detectar cuándo datetimepicker llena el # input-id? Ninguno de los eventos (cambiar, pegar) funciona.
Pathros
¡Esto es genial! Lo probé con jquery 1.8.3 y funciona. Todavía tengo un código livequery () que tengo que reemplazar, así que no puedo actualizar incluso a 1.9.1. Sé que es viejo, pero también lo es todo el sitio web.
Asle
FYI: estos eventos se dispararán con un valor en blanco si el teclado se usa para seleccionar una fecha que no es válida. es decir. Feb 30. stackoverflow.com/questions/48564568/...
Olmstov
107

Desafortunadamente, creo que setIntervalgana el premio:

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

Es la solución más limpia, con solo 1 línea de código. También es el más robusto, ya que no tiene que preocuparse por los diferentes eventos / formas en queinput puede obtener un valor.

Las desventajas de usar 'setInterval' no parecen aplicarse en este caso:

  • ¿La latencia de 100 ms? Para muchas aplicaciones, 100 ms es lo suficientemente rápido.
  • ¿Carga añadida en el navegador? En general, agregar muchos setIntervals pesados ​​en su página es malo. Pero en este caso particular, la carga de página agregada es indetectable.
  • ¿No se escala a muchas entradas? La mayoría de las páginas no tienen más que un puñado de entradas, que puede oler todas en el mismo setInterval.
Dustin Boswell
fuente
21
Esta parece ser la única forma de detectar cambios en un campo de entrada en un formulario cuando se usa el navegador Nintendo 3DS (Versión / 1.7552.EU).
Johan
44
Nota: la sobrecarga se puede aliviar. Si comienza setInterval cuando la entrada obtiene el foco y clearInterval cuando la entrada pierde el foco
Tebe
10
¿Por qué crearía un intervalo de 100 ms en ejecución constante donde no hay razón para que se ejecute constantemente? EVENTOS personas. Usalos, usalos a ellos.
Gavin
15
@Gavin, en este caso particular, JS carece de eventos ... encuéntranos un conjunto de eventos que pueden reconocer, entradas de usuarios, pastas de usuarios, cortes de usuarios, inserciones de JavaScript, eliminaciones de JavaScript, autocompletados de formularios, formularios ocultos, ninguno formas
Naramsim
2
@Gavin JS no parece tener un evento modificado que se active CADA VEZ que se cambie una entrada. (solo haga un document.getElementById (name) .value = Math.random ()) para ver que ninguna de las soluciones de activación de eventos funciona.
Joeri
58

El enlace al oninputevento parece funcionar bien en la mayoría de los navegadores sanos. IE9 también lo admite, pero la implementación tiene errores (el evento no se activa al eliminar caracteres).

Con jQuery versión 1.7+, el onmétodo es útil para vincular el evento de esta manera:

$(".inputElement").on("input", null, null, callbackFunction);
HRJ
fuente
12
oninputel evento no funciona input[type=reset]o la edición de JavaScript, como puede ver en esta prueba: codepen.io/yukulele/pen/xtEpb
Yukulélé
2
@ Yukulélé, al menos podemos solucionarlo más fácilmente que tratar de detectar todas las acciones posibles del usuario .
Pacerier
30

Respuesta de 2017 : el evento de entrada hace exactamente esto para algo más reciente que IE8.

$(el).on('input', callback)
Chris F Carroll
fuente
66
Lo siento pero ... ¿cómo es esto diferente de la respuesta de 2012 de HRJ anterior? De todos modos, el inputevento no puede detectar los cambios de JS.
David
16

Lamentablemente, no hay ningún evento o conjunto de eventos que coincida con sus criterios. Las pulsaciones de teclas y copiar / pegar se pueden manejar con el keyupevento. Los cambios a través de JS son más complicados. Si tiene control sobre el código que establece el cuadro de texto, lo mejor es modificarlo para llamar a su función directamente o activar un evento de usuario en el cuadro 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');
});

Si no tiene control sobre ese código, puede usarlo setInterval()para 'mirar' el cuadro de texto en busca de cambios:

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

Este tipo de monitoreo activo no detectará actualizaciones 'inmediatamente', pero parece ser lo suficientemente rápido como para que no haya un retraso perceptible. Como DrLouie señaló en los comentarios, esta solución probablemente no escala bien si necesita ver muchas entradas. Siempre puede ajustar el segundo parámetro a setInterval()para verificar con mayor o menor frecuencia.

Annabelle
fuente
Para su información, en realidad hay una gran cantidad de eventos que se pueden combinar para que coincidan con los criterios de los OP en distintos grados en todos los navegadores (ver enlaces en el comentario de la primera pregunta). Esta respuesta no usa ninguno de dichos eventos, e irónicamente probablemente funcionará igual de bien en todos los navegadores, aunque con un ligero retraso;)
Crescent Fresh
El OP quiere ver los cambios en el cuadro de texto a través de JavaScript (por ejemplo, myTextBox.value = 'foo';ningún evento de JavaScript maneja esa situación.)
Annabelle
1
¿Qué pasa si alguien tiene 50 campos para controlar?
DoctorLouie
1
Sí, solo tengo 1 campo en mi caso, pero no veo por qué esto no funcionaría razonablemente bien para varios campos. Probablemente sería más eficiente tener solo 1 setTimeout que verifique todas las entradas.
Dustin Boswell
1
@Douglas: ningún evento de JavaScript maneja esa situación - Corrección: IE puede manejarlo input.onpropertychange, y FF2 +, Opera 9.5, Safari 3 y Chrome 1 pueden manejarlo input.__defineSetter__('value', ...)(que, aunque está documentado como no funcional en los nodos DOM, puedo verificar funciona). La única divergencia de la implementación de IE es que IE activa el controlador no solo en la configuración programática sino también durante los eventos clave.
Crescent Fresh
6

Aquí hay una solución ligeramente diferente si no le gustó ninguna de las otras respuestas:

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

Tenga en cuenta que no puede confiar en el clic y el teclado para capturar el menú contextual y pegar.

Peder
fuente
Esta respuesta funcionó mejor para mí. Estaba teniendo algunos problemas con mis selectores hasta que usé: $("input[id^=Units_]").each(function() {
robr
4

Agregue este código en alguna parte, esto hará el truco.

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

Encontré esta solución en val () no desencadena change () en jQuery

lpradhap
fuente
3

He creado una muestra. Que funcione para ti.

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
fuente
3

En realidad, no necesitamos configurar bucles para detectar cambios en javaScript. Ya estamos configurando muchos oyentes de eventos para el elemento que queremos detectar. simplemente desencadenar cualquier evento dañino hará el trabajo

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

y ofc esto solo está disponible si tiene el control total sobre los cambios de javascript en su proyecto.

Serdar
fuente
2

Bueno, la mejor manera es cubrir esas tres bases que enumeró usted mismo. Un simple: onblur,: onkeyup, etc. no funcionará para lo que desea, así que simplemente combínelos.

KeyUp debería cubrir los dos primeros, y si Javascript está modificando el cuadro de entrada, bueno, espero que sea su propio JavaScript, así que solo agregue una devolución de llamada en la función que lo modifica.

idrumgood
fuente
1
+1 para la solución simple, aunque es posible que OP no pueda / no quiera modificar el código haciendo cambios en el cuadro de texto.
Annabelle
Enumerar todos los eventos posibles no me parece robusto. ¿Funciona Keyup si el usuario mantiene presionada la tecla Suprimir (es decir, una tecla de repetición)? ¿Qué pasa con el autocompletado (o barras de herramientas que autocompletan valores)? ¿O hay fuentes de entrada especiales que no activan las pulsaciones de teclas? Me preocuparía que hubiera algo como esto que extrañarías.
Dustin Boswell
1

Aquí hay un ejemplo de trabajo que estoy usando para implementar una variación de autocompletado que llena un selector jqueryui (lista), pero no quiero que funcione exactamente como el autocompletado jqueryui que hace un menú desplegable.

$("#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
fuente
1

NO JQUERY! (De todos modos, es algo obsoleto en estos días)

Editar: eliminé los eventos HTML. NO use eventos HTML ... en lugar de eso use JavaScript Event Listeners.

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>

Señor SirCode
fuente
0

¿No puedes usar <span contenteditable="true" spellcheck="false">element en lugar de <input type="text">?

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

  • No tiene el estilo de un <input>.
  • No tiene una valuepropiedad, pero el texto se representa innerTexty forma parte de su cuerpo interno.
  • Es multilínea, mientras <input>que no lo es aunque establezca el atributo multiline="true".

Para lograr la apariencia, puede, por supuesto, diseñarlo en CSS, mientras escribe el valor como innerText puede obtener para un evento:

Aquí hay un violín .

Desafortunadamente, hay algo que realmente no funciona en IE y Edge, que no puedo encontrar.

Davide Cannizzo
fuente
Creo que existen problemas de accesibilidad en torno al uso de ese método.
Daniel Tonon
0

Aunque esta pregunta se publicó hace 10 años, creo que todavía necesita algunas mejoras. Entonces aquí está mi solución.

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

El único problema con esta solución es que no se activará si el valor cambia de javascript como $('selector').val('some value'). Puede activar cualquier evento a su selector cuando cambia el valor de javascript.

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

puedes ver este ejemplo y elegir cuáles son los eventos que te interesan:

<!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
fuente
44
Creo que olvidó un tipo de evento :)
Dustin Boswell
-3

Puede que llegue tarde a la fiesta aquí, pero ¿no puedes usar el evento .change () que proporciona jQuery?

Deberías poder hacer algo como ...

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

Siempre puedes vincularlo a una lista de controles con algo como ...

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

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

La carpeta en vivo garantiza que se manejen todos los elementos que existen en la página ahora y en el futuro.

Andy Crouch
fuente
2
El evento de cambio solo se dispara después de que se pierde el foco, por lo que no se dispara mientras un usuario escribe en el cuadro, solo después de que haya terminado y continúe haciendo otra cosa.
Eli Sand
2
Como muchas otras respuestas aquí, esto no funcionará cuando el valor del elemento se modifique de Javascript. No sé si se pierde o no cualquier otro caso de modificación que solicitó el OP.
Mark Amery
-4

Esta es la forma más rápida y limpia de hacer eso:

Estoy usando Jquery ->

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

Funciona bastante bien para mí.

Despertaweb
fuente
77
Esto ya ha sido sugerido. Solo trata casos simples, cuando el usuario escribe el texto.
Christophe