Détecter (immédiatement) toutes les modifications apportées à un <type d'entrée = "texte"> à l'aide de JQuery

397

Il existe de nombreuses façons dont la valeur d'un <input type="text">peut changer, notamment:

  • touches
  • copier coller
  • modifié avec JavaScript
  • renseigné automatiquement par un navigateur ou une barre d'outils

Je veux que ma fonction JavaScript soit appelée (avec la valeur d'entrée actuelle) chaque fois qu'elle change. Et je veux qu'il soit appelé immédiatement, pas seulement lorsque l'entrée perd le focus.

Je recherche le moyen le plus propre et le plus robuste de le faire sur tous les navigateurs (en utilisant jQuery de préférence).

Dustin Boswell
la source

Réponses:

352

Ce code jQuery intercepte les modifications immédiates de n'importe quel élément et devrait fonctionner sur tous les navigateurs:

 $('.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
la source
19
Pour la référence de tous, inputc'est l'événement HTML5 qui, je pense, devrait couvrir tous les navigateurs modernes ( référence MDN ). keyupdevrait attraper le reste, mais avec un léger retard ( inputest déclenché sur la touche enfoncée). propertychangeest un événement MS propriétaire, et je ne sais pas si pastec'est réellement nécessaire.
Jo Liss
71
Ai - je tort ou cela ne gère pas lorsque le texte a changé via javascript commedocument.getElementById('txtInput').value = 'some text';
Mehmet Ataş
5
Mehmet, le code n'est pas destiné à intercepter les valeurs modifiées via JS.
phatmann
12
@ MehmetAtaş est en partie correct. La référence de l' inputévénement indique ici qu'il ne se déclenche pas lorsque le contenu est modifié via JavaScript. Cependant, l' onpropertychangeévénement fait feu sur la modification via JS (voir ici ). Ce code fonctionnera donc lorsque le contenu sera modifié via JS sur IE, mais ne fonctionnera pas sur les autres navigateurs.
Vicky Chijwani
2
Vous pouvez déclencher un événement personnalisé sur l'entrée lors du changement de propriété, de la frappe, etc., puis l'utiliser n'importe où.
Ivan Ivković
122

Une solution fantaisie en temps réel pour jQuery> = 1.9

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

si vous souhaitez également détecter l'événement "clic", il suffit de:

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

si vous utilisez jQuery <= 1.4, utilisez simplement liveau lieu de on.

Felix
la source
3
Cela a un inconvénient. Si vous quittez le champ de saisie avec la touche de tabulation - il lancera un événement de touche même si le contenu n'a pas été modifié.
Pawka
2
Comment détecter quand datetimepicker remplit le # input-id? Aucun des événements (modification, collage) ne fonctionne.
Pathros
C'est bien! Je l'ai essayé avec jquery 1.8.3 et cela fonctionne. J'ai encore du code livequery () que je dois remplacer, donc je ne peux pas vraiment passer à la version 1.9.1. Je sais que c'est vieux mais tout le site l'est aussi.
Asle
FYI: Ces événements se déclenchent avec une valeur vide si le clavier est utilisé pour sélectionner une date qui n'est pas valide. c'est à dire. 30. février stackoverflow.com/questions/48564568/...
Olmstov
107

Malheureusement, je pense que setIntervalremporte le prix:

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

C'est la solution la plus propre, avec seulement 1 ligne de code. C'est aussi le plus robuste, car vous n'avez pas à vous soucier de tous les différents événements / façons dont un inputpeut obtenir une valeur.

Les inconvénients de l'utilisation de «setInterval» ne semblent pas s'appliquer dans ce cas:

  • La latence de 100 ms? Pour de nombreuses applications, 100 ms est assez rapide.
  • Charge ajoutée sur le navigateur? En général, l'ajout de nombreux intervalles setIntervals lourds sur votre page est mauvais. Mais dans ce cas particulier, le chargement de page ajouté est indétectable.
  • Il ne s'adapte pas à de nombreuses entrées? La plupart des pages n'ont pas plus d'une poignée d'entrées, que vous pouvez renifler toutes dans le même setInterval.
Dustin Boswell
la source
21
Cela semble être le seul moyen de détecter les modifications apportées à un champ de saisie dans un formulaire lors de l'utilisation du navigateur Nintendo 3DS (Version / 1.7552.EU).
Johan
4
Remarque: la surcharge peut être atténuée. Si vous démarrez setInterval lorsque l'entrée obtient le focus et clearInterval lorsque l'entrée perd le focus
Tebe
10
Pourquoi voudriez-vous créer un intervalle de 100 ms en cours d'exécution sans aucune raison pour qu'il s'exécute en permanence? ÉVÉNEMENTS personnes. Utilise les.
Gavin
15
@Gavin, dans ce cas particulier JS manque d'événements .. trouvez-nous un ensemble d'événements qui peuvent reconnaître, entrées utilisateur, pâtes utilisateur, coupes utilisateur, insertions javascript, suppressions javascript, auto-complétion de formulaires, formulaires masqués, non affichés formes.
Naramsim
2
@Gavin JS ne semble pas avoir d'événement onchanged qui se déclenche CHAQUE FOIS qu'une entrée est modifiée. (faites juste un document.getElementById (name) .value = Math.random ()) pour voir qu'aucune des solutions de déclenchement d'événement ne fonctionne.
Joeri
58

La liaison à l' oninputévénement semble fonctionner correctement dans la plupart des navigateurs sensés. IE9 le prend également en charge, mais l'implémentation est boguée (l'événement n'est pas déclenché lors de la suppression de caractères).

Avec jQuery version 1.7+, la onméthode est utile pour se lier à l'événement comme ceci:

$(".inputElement").on("input", null, null, callbackFunction);
HRJ
la source
12
oninputl'événement ne fonctionne pas avec input[type=reset]ou l'édition javascript comme vous pouvez le voir dans ce test: codepen.io/yukulele/pen/xtEpb
Yukulélé
2
@ Yukulélé, Au moins, nous pouvons contourner cela plus facilement que d'essayer de détecter toutes les actions possibles des utilisateurs .
Pacerier
30

Réponse de 2017 : l' événement d'entrée fait exactement cela pour tout ce qui est plus récent que IE8.

$(el).on('input', callback)
Chris F Carroll
la source
6
Désolé mais ... en quoi est-ce différent de la réponse 2012 de HRJ ci-dessus? Quoi qu'il en soit, l' inputévénement ne parvient pas à détecter les modifications JS.
David
16

Malheureusement, aucun événement ou ensemble d'événements ne correspond à vos critères. Les frappes au clavier et le copier / coller peuvent tous deux être gérés avec l' keyupévénement. Les modifications via JS sont plus délicates. Si vous contrôlez le code qui définit la zone de texte, le mieux est de le modifier pour appeler directement votre fonction ou déclencher un événement utilisateur sur la zone de texte:

// 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 vous n'avez aucun contrôle sur ce code, vous pouvez utiliser setInterval()pour «surveiller» la zone de texte pour les modifications:

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

Ce type de surveillance active n'attrapera pas les mises à jour «immédiatement», mais il semble être assez rapide pour qu'il n'y ait pas de décalage perceptible. Comme l'a souligné DrLouie dans les commentaires, cette solution n'est probablement pas très évolutive si vous devez surveiller de nombreuses entrées. Vous pouvez toujours régler le 2e paramètre setInterval()pour vérifier plus ou moins fréquemment.

Annabelle
la source
Pour info, il existe en fait une multitude d'événements qui peuvent être combinés pour correspondre aux critères des PO à des degrés divers selon les navigateurs (voir les liens dans le commentaire de la première question). Cette réponse n'utilise aucun desdits événements et, ironiquement, fonctionnera probablement aussi bien dans tous les navigateurs, bien qu'avec un léger décalage;)
Crescent Fresh
L'OP souhaite intercepter les modifications apportées à la zone de texte via JavaScript (par exemple, myTextBox.value = 'foo';aucun événement JavaScript ne gère cette situation.
Annabelle
1
Et si quelqu'un avait 50 champs pour garder un œil?
DoctorLouie
1
Oui, je n'ai qu'un seul champ dans mon cas, mais je ne vois pas pourquoi cela ne fonctionnerait pas assez bien pour un certain nombre de champs. Il serait probablement plus efficace de n'avoir qu'un seul setTimeout qui vérifie toutes les entrées.
Dustin Boswell
1
@Douglas: Aucun événement JavaScript ne gère cette situation - Correction: IE peut le gérer avec input.onpropertychange, et FF2 +, Opera 9.5, Safari 3 et Chrome 1 peuvent le gérer avec input.__defineSetter__('value', ...)(qui, bien qu'il soit documenté comme non fonctionnel sur les nœuds DOM, je peux vérifier Ça marche). La seule divergence par rapport à l'implémentation d'IE est qu'IE déclenche le gestionnaire non seulement sur le paramétrage programmatique mais également pendant les événements clés.
Crescent Fresh
6

Voici une solution légèrement différente si vous n'avez pas aimé les autres réponses:

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

Considérez que vous ne pouvez pas vous fier aux clics et aux touches pour capturer le collage du menu contextuel.

Peder
la source
Cette réponse a fonctionné le mieux pour moi. J'avais des problèmes avec mes sélecteurs jusqu'à ce que j'utilise: $("input[id^=Units_]").each(function() {
robr
4

Ajoutez ce code quelque part, cela fera l'affaire.

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

Trouvé cette solution à val () ne déclenche pas change () dans jQuery

lpradhap
la source
3

J'ai créé un échantillon. Que cela fonctionne pour vous.

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
la source
3

En fait, nous n'avons pas besoin de configurer de boucles pour détecter les modifications javaScript. Nous configurons déjà de nombreux écouteurs d'événements pour l'élément que nous voulons détecter. il suffit de déclencher un événement non nocif pour faire le travail.

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

et ofc cela n'est disponible que si vous avez le contrôle total sur les modifications javascript sur votre projet.

Serdar
la source
2

Eh bien, la meilleure façon est de couvrir vous-même ces trois bases que vous avez énumérées. Un simple: onblur,: onkeyup, etc. ne fonctionnera pas pour ce que vous voulez, alors combinez-les simplement.

KeyUp devrait couvrir les deux premiers, et si Javascript modifie la zone de saisie, eh bien j'espère que c'est votre propre javascript, alors ajoutez simplement un rappel dans la fonction qui le modifie.

idrumgood
la source
1
+1 pour la solution simple, bien qu'il soit possible qu'OP ne puisse pas / ne veuille pas modifier le code en apportant des modifications à la zone de texte.
Annabelle
Énumérer tous les événements possibles ne me semble pas solide. La touche fonctionne-t-elle si l'utilisateur maintient la touche Suppr enfoncée (c'est-à-dire une touche de répétition)? Qu'en est-il de la saisie semi-automatique (ou des barres d'outils qui remplissent automatiquement les valeurs)? Ou existe-t-il des sources d'entrée spéciales qui ne déclenchent pas les touches? Je serais inquiet qu'il y aurait quelque chose comme ça que vous manqueriez.
Dustin Boswell
1

Voici un exemple de travail que j'utilise pour implémenter une variante de saisie semi-automatique qui remplit un sélecteur jqueryui (liste), mais je ne veux pas qu'il fonctionne exactement comme la saisie semi-automatique jqueryui qui fait un menu déroulant.

$("#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>");
            }
        }); 
});
clair
la source
1

PAS DE JQUERY! (Son genre obsolète de nos jours de toute façon)

Edit: j'ai supprimé les événements HTML. N'utilisez PAS d'événements HTML ... utilisez plutôt des écouteurs d'événement 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>

Monsieur SirCode
la source
0

Ne pouvez-vous pas simplement utiliser l' <span contenteditable="true" spellcheck="false">élément à la place de <input type="text">?

<span>(avec contenteditable="true" spellcheck="false"comme attributs) se distingue <input>principalement par:

  • Ce n'est pas un style <input>.
  • Il n'a pas de valuepropriété, mais le texte est rendu comme innerTextet fait partie de son corps intérieur.
  • C'est multiligne, contrairement à ce <input>que vous définissez l'attribut multiline="true".

Pour accomplir l'apparence, vous pouvez, bien sûr, le styliser en CSS, tout en écrivant la valeur que innerTextvous pouvez obtenir pour lui un événement:

Voici un violon .

Malheureusement, il y a quelque chose qui ne fonctionne pas réellement dans IE et Edge, que je ne trouve pas.

Davide Cannizzo
la source
Je pense que l'utilisation de cette méthode pose des problèmes d'accessibilité.
Daniel Tonon
0

Bien que cette question ait été publiée il y a 10 ans, je pense qu'elle doit encore être améliorée. Voici donc ma solution.

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

Le seul problème avec cette solution est qu'elle ne se déclenchera pas si la valeur change de javascript comme $('selector').val('some value'). Vous pouvez déclencher n'importe quel événement sur votre sélecteur lorsque vous modifiez la valeur à partir de javascript.

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

vous pouvez voir cet exemple et choisir quels sont les événements qui vous intéressent:

<!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
la source
4
Je pense que vous avez oublié un type d'événement :)
Dustin Boswell
-3

Je suis peut-être en retard à la fête ici, mais ne pouvez-vous pas simplement utiliser l'événement .change () fourni par jQuery.

Vous devriez pouvoir faire quelque chose comme ...

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

Vous pouvez toujours le lier à une liste de contrôles avec quelque chose comme ...

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

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

Le classeur en direct garantit que tous les éléments qui existent sur la page maintenant et à l'avenir sont traités.

Andy Crouch
la source
2
L'événement change ne se déclenche qu'après la perte du focus, il ne se déclenche donc pas lorsqu'un utilisateur tape dans la zone, seulement après avoir terminé et continue à faire autre chose.
Eli Sand
2
Comme beaucoup d'autres réponses ici, cela ne fonctionnera pas lorsque la valeur de l'élément est modifiée à partir de Javascript. Je ne sais pas s'il manque ou non d'autres cas de modification demandés par le PO.
Mark Amery
-4

C'est le moyen le plus rapide et le plus propre de le faire:

J'utilise Jquery ->

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

Cela fonctionne très bien pour moi.

Despertaweb
la source
7
Cela a déjà été suggéré. Il ne traite que des cas simples, lorsque l'utilisateur tape le texte.
Christophe