Bonjour,

Je suis en train de travailler sur un gestionnaire d'habilitation qui va permettre aux utilisateurs enregistrés d'accéder à une ou plusieurs applications en ligne.
J’ai créé un formulaire qui permet de sélectionner un utilisateur d'autoriser son accès à une application web.
Le formulaire est on ne peut plus simple et tous les échanges avec la base de données s’effectuent avec AJAX.

Mon formulaire

Dans mon formulaire j'ai :
Un évènement attaché à un menu déroulant (bootstrap-select) qui permet de sélectionner un utilisateur et de récupérer de la table sql des utilisateurs les informations dont j'ai besoin.

<div class="container">
    <h1>Ajouter un utilisateur</h1>
    <div id="addform" class="col-lg-6">
        <form action="#" class="form-horizontal" enctype="application/x-www-form-urlencoded">
            <div class="form-group">
                <label class="control-label col-lg-4" for="identite">Identifiant : </label>
                <div class="col-lg-8">
                    <select id="idselection" class="selectpicker" data-live-search="true" title="Matricule/Nom" data-width="auto">
                        <option selected disabled ></option>
                        <?php foreach ($userslist as $item): ?>
                        <option value="<?= $item->matricule; ?>" ><?= $item->nom; ?> <?= $item->prenom; ?> <?= $item->matricule; ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
            </div>
        </form>
        <div id="user" data-app="<?= is_numeric($this->request->params[0]) ? $this->request->params[0] : null; ?>"></div>
    </div>
</div>

Le résultat de la requête est affiché dans le div#user par chargement d'un fichier php userprofile.php dans lequel j'insère les informations :

<div class="userprofilebox">
    <div class="input-group">
        <span class="input-group-addon">NOM : </span>
        <p id="nom" class="form-control"></p>
    </div>
    <div class="input-group">
        <span class="input-group-addon">PRENOM : </span>
        <p id="prenom" class="form-control"></p>
    </div>
    <div class="input-group">
        <span class="input-group-addon">MATRICULE : </span>
        <p id="matricule" class="form-control"></p>
    </div>
    <div class="input-group">
        <span class="input-group-addon">SERVICE : </span>
        <p id="service" class="form-control"></p>
    </div>
    <div class="row">
        <button id="validate" class="btn btn-success" type="button" value="" ><span class="glyphicon glyphicon-ok"></span> Habiliter</button>
        <a class="btn btn-danger" href="<?= WEBROOT; ?>/app/liste" target="_parent" title="Retour à la liste des applications" ><span class="glyphicon glyphicon-remove"></span> Abandon</a>
    </div>
</div>

..et je clique sur le bouton Habiliter pour enregistrer cette nouvelle habilitation.

Mon Jquery

$(function () {

    // Selection de l’utilisateur

    $("#idselection").on('changed.bs.select', function (e) {
            var identifiant = $(this).val();
            function reponse(callback) {
                  $.ajax({
                          url: "applications/activedirectory/PHPScripts/ajax-get-user", // requete sur table des utilisateurs
                          method: "post",
                          data: {ref: identifiant},
                          dataType: 'JSON'
                  })
                    .done(function (data, textStatus, jqxhr) {
                            callback(data);
                    })
                    .fail(function (jqxhr) {
                            callback(jqxhr.responseText);
                    });
            }

// Resultat de la requet est affiché dans la page  userprofile.php chargée dans un div (J'avais la flême de générer du DOM)
            reponse(function (data) {
                    var app = $("#user").data('app');
                    $("#user")
                                      .load('userprofile', function (e) {
                                              $(this).find('#nom').text(data.nom);
                                              $(this).find('#prenom').text(data.prenom);
                                              $(this).find('#matricule').text(data.matricule);
                                              $(this).find('#service').text(data.service);
                                              $(this).find('#validate').attr('value', data.matricule); // Bouton de validation 
                                      })

                    // Click sur le bouton de validation
                                        .on('click', 'button#validate', function (e) {
                                                function calling(callback) {

                                                        // Requete INSERT dans la table des habilitations
                                                        $.ajax({
                                                            url: "applications/activedirectory/PHPScripts/ajax-add-user", 
                                                            method: "post",
                                                            data: {app: app, ref: identifiant},
                                                            dataType: 'JSON'
                                                        })

                                                          .done(function (data, textStatus, jqxhr) {
                                                              callback(data);
                                                          })

                                                          .fail(function (jqxhr) {
                                                              callback(jqxhr.responseText);
                                                          });
                                        }

                                    // Affichage d’un message (erreur ou succès)
                                      calling(function (data) {
                                          var msg = "<div class='col-lg-12 alert alert-info'>";
                                          msg += "<button type='button' class='close' aria-label='Close' data-dismiss='alert'><span aria-hidden='true'>&times;</span></button>";
                                          msg += "<h4>Message</h4>";
                                          msg += "<p>" + data +"</p>";
                                          msg += "</div>";

                                          $("#user").before(msg);
                                      });
                            });
                });
        })
});

Ce que j'obtiens

Tous fonctionne à merveille (hormis les messages mais ça c'est un détail)  ! Sauf… que dans la console du navigateur (network onglet XHR) je vois apparaître l’impensable ! l’inimaginable !

Dans mon formulaire je sélection un utilisateur, les informations s'affichent, je clique sur le bouton button#validate, il est enregistré dans la table des utilisateurs habilités.

Je continue, je sélectionne un autre utilisateur, les informations de ce nouvel utilisateur remplacent les précédentes, je clique sur le bouton button#validate, il est enregistré.

etc.. donc je suis content !

MAIS !
Voilà ce qu'il se passe lorsque je surveille l'activité dans la console du navigateur -> network onglet XHR
Premier utilisateur : une requête vers ajax-add-user.php
Deuxième utilisateur : je vois 2 requêtes vers ajax-add-user.php ( le premier utilisateur suivi du second)
Troisième : je vois 3 requêtes vers ajax-add-user.php… (le premier suivi du second lui-même suivi du troisième).

etc..

Bon, j’ai cherché avant de poster sur le forum mais là… je donne ma langue au chat.
Jquery bubble ? Heu non j’pense pô.

Ma seule piste c’est la délégation du click sur le bouton #validate.
Pour l'heure j'ai réecrit le code jquery différement en séparant les contenus de l'évenement change et click

$(function(){
        $("#idselection").on('changed.bs.select', function (e) {...}
        $(#user).on('click', 'button#validate', function (e) {..}
});

C'est moche mais ça fonctionne aussi.
Si quelqu'un pouvait m'expliquer d'ou vient cette démultiplication du contenu de l'évènement CLICK, ça serait simpa :)

6 réponses


Maenhyr
Réponse acceptée

Hello, cet article résume tout à fait le comportement du bug et pourquoi ton changement a résolu le problème.https://triangle717.wordpress.com/2015/12/14/js-avoid-duplicate-listeners/

C'est parce que dans tu ajoutes un listener sur click a chaque fois dans ton callback de reponse().

Bonjour et merci de ta réponse !
Cela je m'en doutais un peu ("Ma seule piste c’est la délégation du click sur le bouton #validate.") mais ce que j'ignore c'est le comment du pourquoi. A chaque selection d'un nouvel utilisateur les variables destinées à transmettre les données en POST par AJAX sont réinitialisées non ? Comment se peut il que les réponses AJAX précédentes soient toujours rappelées lors d'une nouvelle excution du code ? C'est ça que je ne comprend pas...

Définit ton évènement click une seule fois et utilise des variables...
Si tu ne connais rien à javascript arrête d'utiliser jquery -_-
Apprends js et ensuite tu pourras légitimement, facilement et de façon transparente te "faciliter" la vie avec jquery ou n'importe quel autre framework..!

Merci prbaron, voilà une réponse tout à fait productive et qui va me permettre d'avancer dans mon apprentissage !
Psylozoff, je me passerais volontier de ce genre de commentaires d'autant que la solution ne m'intéresse pas, mais plutôt une piste de reflexion , un lien vers un article, afin comprendre mon erreur.

Tu dis que tu ne comprends pas la notion de "gestionnaire d'évènement" qui est pourtant une des bases fondamentales de javascript ^^
Or, tu nous présentes du jQuery : un framework qui occulte la majeure partie de la logique applicative de JS :-s
Je te dis juste que tu essaies de comprendre le fonctionnement d'un moteur en regardant la carrosserie...