Pourquoi l'événement jquery change ne se déclenche-t-il pas lorsque je définit la valeur d'une sélection à l'aide de val ()?

377

La logique du change()gestionnaire d'événements n'est pas exécutée lorsque la valeur est définie par val(), mais elle s'exécute lorsque l'utilisateur sélectionne une valeur avec sa souris. Pourquoi est-ce?

<select id="single">
    <option>Single</option>
    <option>Single2</option>
</select>

<script>
    $(function() {
        $(":input#single").change(function() {
           /* Logic here does not execute when val() is used */
        });
    });

    $("#single").val("Single2");
</script>
Eric Belair
la source

Réponses:

589

Parce que l' changeévénement nécessite un événement de navigateur réel initié par l'utilisateur au lieu de via le code javascript.

Faites ceci à la place:

$("#single").val("Single2").trigger('change');

ou

$("#single").val("Single2").change();
user113716
la source
1
Salut, faire cette mise à jour déroulante. mais être appelé onChange () récursivement.
Pankaj
3
Je creuse cette réponse. Cela fonctionne, mais n'y a-t-il pas un meilleur moyen avec jQuery? Je suis sûr que nous nous souviendrons TOUS de le faire chaque fois que nous modifions une valeur qui a un gestionnaire de changement configuré (sarcasme). Les cadres qui utilisent la liaison de données pourraient-ils mieux résoudre ce problème?
Jess
@ user113716 sauriez-vous comment le déclencher sans connaître aucune valeur?
BKSpurgeon
4
Je suis d'accord que c'est la bonne réponse, mais cela suce et ruine absolument ce que j'essaie de faire.
Dustin Poissant
Essayez $ ("# single"). Trigger ({type: "change", val: "Single2"})
user2901633
44

Je pense que vous pouvez déclencher manuellement l'événement de modification avec trigger():

$("#single").val("Single2").trigger('change');

Bien que pourquoi il ne se déclenche pas automatiquement, je n'en ai aucune idée.

David dit de réintégrer Monica
la source
Je ne suis pas en mesure de faire fonctionner cela, aussi $ ("# single"). Val ("Single2"). Change (); Je crée dynamiquement une liste déroulante et modifie sa valeur (cela fonctionne bien pour moi) Mais quand j'essaie de déclencher un changement, cela ne fonctionne pas pour moi.
Maneesh Kumar
4
Un "bit" tardif mais il ne se déclenche pas automatiquement car cela entraînerait des boucles infinies. Pensez$('#foo').change(function() { $( this ).val( 'whatever' ); });
JJJ
@Juhana Je pense qu'une boucle infinie est beaucoup plus facile à remarquer, à diagnostiquer et à corriger que val () échouant mystérieusement à déclencher des "changements" de liaisons.
iono
Appeler .change () est un raccourci pour appeler .trigger ('changer').
Triynko
9

L'ajout de ce morceau de code après le val () semble fonctionner:

$(":input#single").trigger('change');
Eric Belair
la source
6
Oui, mais vous n'avez pas besoin de faire une sélection DOM séparée pour cela $(":input#single"). En fait, vous ne devriez vraiment pas. Il suffit de l'enchaîner après le .val(). Vous pouvez utiliser .trigger('change')ou .change(). Ce sont exactement les mêmes.
user113716
8

Autant que je puisse lire dans les API. L'événement n'est déclenché que lorsque l'utilisateur clique sur une option.

http://api.jquery.com/change/

Pour les cases de sélection, les cases à cocher et les boutons radio, l'événement est déclenché immédiatement lorsque l'utilisateur effectue une sélection avec la souris, mais pour les autres types d'élément, l'événement est différé jusqu'à ce que l'élément perd le focus.

Marnix
la source
Quelles sont les alternatives pour les autres types d'éléments? Je veux que l'événement «change» se déclenche immédiatement pour les inputéléments, pas lorsque l'élément perd le focus. Savez-vous?
Dan Nissenbaum
1
Si par "immédiatement" vous voulez dire "chaque fois qu'une touche est enfoncée dans l'entrée", vous recherchez les événements onkeyup, onkeypress et onkeydown, pas en cas de changement.
user2867288
6

Pour le rendre plus facile, ajoutez une fonction personnalisée et appelez-la quand vous le souhaitez, la modification de la valeur déclenche également le changement

$.fn.valAndTrigger = function (element) {
    return $(this).val(element).trigger('change');
}

et

$("#sample").valAndTirgger("NewValue");

Ou vous pouvez remplacer la fonction val pour toujours appeler la modification lorsque le val est appelé

(function ($) {
    var originalVal = $.fn.val;
    $.fn.val = function (value) {
        this.trigger("change");
        return originalVal.call(this, value);
    };
})(jQuery);

Exemple sur http://jsfiddle.net/r60bfkub/

Alireza Fattahi
la source
Mon navigateur se plaint de trop de récursivité, une solution pour cela?
Bhargav Nanekalva
Vous écoutez l' changeévénement -événement sur un contrôle et exécutez un rappel qui (directement ou indirectement) déclenche à nouveau l' changeévénement -événement sur le même contrôle. Je vous suggère d'utiliser un autre nom pour l'événement que vous déclenchez manuellement (par exemple explicit.change), afin qu'il ne déclenche pas à nouveau l' changeévénement, empêche la boucle et vous permette toujours de réagir à l'événement.
Kamafeather
3

Si vous ne souhaitez pas confondre avec un événement de modification par défaut, vous pouvez fournir votre événement personnalisé

$('input.test').on('value_changed', function(e){
    console.log('value changed to '+$(this).val());
});

pour déclencher l'événement sur la valeur définie, vous pouvez le faire

$('input.test').val('I am a new value').trigger('value_changed');
rythme de codage
la source
3

J'ai rencontré le même problème lors de l'utilisation de CMB2 avec Wordpress et je voulais me connecter à l'événement de changement d'une métabox de téléchargement de fichier.

Donc, si vous n'êtes pas en mesure de modifier le code qui appelle la modification (dans ce cas, le script CMB2), utilisez le code ci-dessous. Le déclencheur est invoqué APRÈS que la valeur est définie, sinon votre changement eventHandler fonctionnera, mais la valeur sera la précédente, pas celle qui est définie.

Voici le code que j'utilise:

(function ($) {
    var originalVal = $.fn.val;
    $.fn.val = function (value) {
        if (arguments.length >= 1) {
            // setter invoked, do processing
            return originalVal.call(this, value).trigger('change');
        }
        //getter invoked do processing
        return originalVal.call(this);
    };
})(jQuery);
user2075039
la source
C'est bien, mais pouvez-vous montrer comment vous le limiteriez à une seule entrée particulière?
jwebb
@jwebb Vous ne savez pas trop ce que vous voulez dire? Nous remplaçons la fonction jQuery val (). Vous liez un gestionnaire d'événements de changement à une entrée spécifique.
user2075039
Je veux dire, @ user2075039, que se passe-t-il s'il y a plusieurs entrées sur la page en utilisant la fonction jQuery val () et que vous ne voulez pas lier le gestionnaire d'événements de changement personnalisé à TOUS, mais à l'un d'entre eux seulement?
jwebb
@jwebb vous pouvez vérifier this.selectorà l'intérieur de la $.fn.valfonction prioritaire , pour un sélecteur spécifique (dépendra de ce qui est utilisé pour le sélecteur). C'est le meilleur que j'ai trouvé pour faire cela uniquement pour des domaines spécifiques, le seul problème est que vous devez savoir ce qu'ils utilisent pour le sélecteur
sMyles
1
$(":input#single").trigger('change');

Cela a fonctionné pour mon script. J'ai 3 combos et je lie avec l'événement chainSelect, je dois passer 3 valeurs par URL et par défaut sélectionner tout déroulant. J'ai utilisé ça

$('#machineMake').val('<?php echo $_GET['headMake']; ?>').trigger('change');

Et le premier événement a fonctionné.

Prashant Patil
la source
10
J'espère que vous ne faites jamais ça dans le monde réel. Inutile de dire que $ _GET ['any'] ne peut pas faire confiance.
Denis V
1

Si vous venez d'ajouter l'option de sélection à un formulaire et que vous souhaitez déclencher l'événement de modification, j'ai trouvé qu'un setTimeout est requis sinon jQuery ne récupère pas la boîte de sélection nouvellement ajoutée:

window.setTimeout(function() { jQuery('.languagedisplay').change();}, 1);
Dave Hilditch
la source