J'ai le menu déroulant "Titre" qui, lorsqu'il est sélectionné sur "Dr", déclenche l'affichage du menu déroulant "Sexe". Sur la page prête, j'attache des gestionnaires d'événements pour changer l'événement, mais il existe une sorte de mécanisme sur la page (que je ne contrôle pas) qui change la valeur de la liste déroulante par programmation mais ne déclenche pas l'événement change().

L'appel manuel .change() une fois la valeur définie n'est pas une option.

Comment attacher un gestionnaire d'événements JavaScript qui est déclenché lorsque la valeur est modifiée par programme, au lieu de seulement lorsqu'elle est modifiée via l'interface utilisateur de select?

2
Matas Vaitkevicius 15 juil. 2015 à 14:06

2 réponses

Meilleure réponse

Vous ne pouvez pas recevoir de notification d'événement si le code que vous ne contrôlez pas modifie la valeur sélectionnée dans la zone select. Vous vous retrouvez avec l'option désagréable d'interroger via setInterval ou une série chaînée de setTimeout.

J'ai vérifié, et même un MutationObserver n'aide pas - ce qui n'est pas surprenant car changer l'élément sélectionné ne mute pas le DOM, mais j'espérais quand même.

L'interroger toutes les 50 ms environ ne devrait pas avoir d'incidence significative sur les performances.

Voici un exemple d'interrogation, modifiez l'intervalle comme il convient:

(function() {
  var s = $("#s"),
      lastValue = s.val();
  setInterval(function() {
    var value = s.val();
    if (value !== lastValue) {
      lastValue = value;
      snippet.log("The value changed to: " + value);
    }
  }, 50);
})();
snippet.log("Changing the value in a second...");
setTimeout(function() {
  snippet.log("Changing value now");
  $("#s").val("2");
  snippet.log("Done changing value");
}, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<select id="s">
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Re votre commentaire à Amit:

knockout.js le fait avec les observables en quelque sorte

Non, ce n'est pas le cas, cela dépend de votre seule modification de la valeur de la sélection via la boîte de sélection (qui déclenche un événement) ou via KO:

var vm = {
  sval: ko.observable('one')
};
vm.sval.subscribe(function(newValue) {
  snippet.log("New value: " + newValue);
});
ko.applyBindings(vm, document.body);
snippet.log("sval is " + vm.sval() + ", changing value in select in one second");
setTimeout(function() {
  document.getElementById("s").selectedIndex = 1;
  snippet.log("Changed select, but sval is still " + vm.sval());
}, 1000);
<select id="s" data-bind="value: sval, options: ['one', 'two', 'three']"></select>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
2
T.J. Crowder 15 juil. 2015 à 12:14

Ceci est une modification de réponse par @ T.JCrowder pour ceux qui ont besoin d'une version qui déclenche .change() lors du premier changement d'option de liste déroulante puis s'arrête.

(function () {
    var selector = "[id='Title']";
    var lastValue = $(selector).val();
    var timer = setInterval(function () {
        var value = $(selector).val();
        if (value !== lastValue) {
            lastValue = value;
            $(selector).change();
            clearInterval(timer);
        }
    }, 500);
})();
0
Matas Vaitkevicius 15 juil. 2015 à 15:41