IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

AJAX Discussion :

Evénement sur éléments crées dynamiquement


Sujet :

AJAX

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mars 2016
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2016
    Messages : 95
    Par défaut Evénement sur éléments crées dynamiquement
    Bonjour,

    J'ai une page web, qui peut déclencher une requête Ajax.
    Cette requête Ajax renvoie du code HTML, qui devient l'innerHTML d'une DIV.
    Dans ce code renvoyé par l'Ajax, il y a des boutons, auxquels je souhaite associer des événements.
    Je vais chercher les boutons par un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    querySelector(identifiantCss);
    Jusque là, ça marche. Par exemple, lorsque je demande à la console.log d'afficher le bouton.value, il me rend bien le texte du bouton en console.

    Là où ça ne va plus, c'est lorsque j'ajoute un événement à mon bouton.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monBouton.addEventListener('click', actionBouton);
    Pourtant, la procédure-cible est bien visée. Si je la renomme, pour qu'elle ne corresponde plus à la fonction invoquée, je vois une erreur en console.
    Mais quand je clique sur le bouton, rien ne se passe.

    Voici la procédure invoquée comme event handler :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    actionImporteTousParag = function(e)
     {
      console.log('Importer tous les paragraphes du modèle ');
     },
    Voici la procédure qui reçoit la requête Ajax :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     actionXhrModele = function(e)
     {
      if(e.target.readyState == 4)
      {
       if(e.target.status == 200)
       {
        divImport.innerHTML = e.target.responseText;
        leBoutonTousParag = document.querySelector('article#artiImport div.scrollable input[type=button]#tous');
        leBoutonTousParag.addEventListener('click', actionImporteTousParag);
        console.log(leBoutonTousParag.value); // OK, il affiche "Tous"
       }
       else
       {
        divImport.innerHTML = '<P style="text-align:center;background-color:rgba(255, 0, 0, 0.6);">Echec requ&ecirc;te ajax</P>';
       }
       divImport.innerHTML += '<P style="text-align:center;">' + (Date.now() - delai) + ' ms</P>';
      }
     },
    Cette procédure en réponse à une requête Ajax (onreadystatechange) ne pose aucun problème, elle est bien appelée et exécutée.
    Elle donne un contenu à la DivImport

    leBoutonTousParag a bien été récupéré par le
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    document.querySelector('article#artiImport div.scrollable input[type=button]#tous');
    Puisqu'à la ligne suivante, le console.log affiche bien sa Value en console (le texte sur le bouton est 'Tous', je le retrouve bien en console.)

    Cependant, lorsque je le clique, rien n'est déclenché.
    Je doute qu'il ait associé la fonction à l'événement.
    Dubitatif, je renomme la fonction en "actionImporteTousParag2" (mais pas dans le paramètre de addEventListener) et ça déclenche une erreur, comme dit plus haut, puisqu'il ne trouve plus la fonction événementielle.
    Donc, c'est qu'il l'associait bien au bouton.

    J'essaie avec d'autres événements que click : blur, mouseenter, focus, ...
    Rien n'y fait, le bouton reste sourd.

    Pourtant, j'ai la certitude :
    • Que le bouton existe (Je le vois)
    • Qu'il est récupéré par le QuerySelector, puisqu'il affiche sa 'Value' en console.
    • Qu'un événement y est bien associé, puisque si je change le nom de la fonction associée, une erreur a lieu.


    A mon avis, ça doit être une question de propagation par bouillonnement ou capture.
    J'ai essayé d'ajouter un troisième paramètre à mon addEventListener. false ou true n'y change rien.

    Quelqu'un saurait-il m'expliquer comment associer un événement à un bouton importé, après coup, dans une page web, par une requête ajax ?

    Merci,
    Christian.

  2. #2
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mars 2016
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2016
    Messages : 95
    Par défaut Je devrais poser ma question autrement
    Pour résumer : comment associer un événement à un nouvel élément HTML, rajouté ultérieurement par une requête Ajax ?

  3. #3
    Expert confirmé
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 102
    Par défaut
    Bonjour,
    comme tu l’as peut-être constaté, innerHTML, utilisé en écriture, détruit le contenu de l’élément. Il est souvent plus astucieux d’utiliser insertAdjacentHTML.
    Mais ce n’est pas suffisant dans le cas d’un texte (et j’insiste sur le terme texte, la propriété responseText ne s’appelle pas comme ça pour rien) renvoyé par ajax : lorsque ce texte est interprété comme du code HTML, une nouvelle représentation DOM est crée, et le moteur JavaScript ne peut pas deviner ce que tu as l’intention d’en faire, notamment les gestionnaires d’évènements que tu veux attacher dessus. C’est à toi de le faire explicitement.

    Il faut commencer par sélectionner le bouton nouvellement inséré :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const nouveauBouton = divImport.querySelector('button'); // à raffiner si besoin
    Puis tu lui attaches le gestionnaire d’évènement, exactement comme pour le premier bouton :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    nouveauBouton.addEventListener('click', actionBouton);
    Attention avec les id : je pense que les sélecteurs que tu utilises actuellement sont inutilement compliqués. Dès lors qu’ils contiennent un sélecteur d’id (#tous en l’occurence), le reste est superflu, et cause même une diminution de performance lors de la sélection CSS.

    Et si tu utilises plusieurs fois le même id, tu perds l’avantage des id, autant utiliser des classes. Ou alors, rajoute une partie variable pour les rendre vraiment uniques, par exemple #tous1, #tous2, etc.

    Une dernière remarque : soit tes variables ne sont pas déclarées, soit tu ne montres pas le code où tu les déclares. Je te conseille d’utiliser le mode strict qui permet d’éviter un certain nombre de mauvaises pratiques.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  4. #4
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mars 2016
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2016
    Messages : 95
    Par défaut Ca ne fonctionne pas non plus
    Merci Watilin pour ta réponse, mais ça ne marche pas non plus.
    Voici ma procédure qui réceptionne la chaîne d'Ajax, et l'insère dans le DOM, comme contenu de ma divImport

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
     actionXhrModele = function(e)
     {
      if(e.target.readyState == 4)
      {
       if(e.target.status == 200)
       {
        //divImport.innerHTML = e.target.responseText;
        divImport.innerHTML = '';
        divImport.insertAdjacentHTML('afterbegin', e.target.responseText);
        const leBoutonTousParag = divImport.querySelector('#tous');
        console.log(leBoutonTousParag.value); // OK, il affiche "Tous"
        leBoutonTousParag.addEventListener('click', actionImporteTousParag);
        //leBoutonTousParag.addEventListener('click', function(e){console.log('Test fonction inline');});
     
       }
       else
       {
        divImport.innerHTML = '<P style="text-align:center;background-color:rgba(255, 0, 0, 0.6);">Echec requ&ecirc;te ajax</P>';
       }
       divImport.innerHTML += '<P style="text-align:center;">' + (Date.now() - delai) + ' ms</P>';
      }
     },
    ...
    Au lieu de donner un innerHTML à ma divImport, j'utilise, sur ton conseil, insertAdjacentHTML.
    Je dois donc spécifier la position d'insertion en premier paramètre.
    Comme il y avait déjà du contenu "Attente requête Ajax" dans ma divImport, je la vide au préalable.
    Car insertAdjacentHTML ajoute, sans écraser, alors qu'en définissant l'innerHTML, j'écrasais le contenu précédent.

    J'ai également simplifié mon sélecteur CSS, mais le précédent, plus long, ne posait pas problème.
    J'en veux pour preuve la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    console.log(leBoutonTousParag.value); // OK, il affiche "Tous"
    qui affichait bien la value de mon bouton dans la console.
    D'autre part, et ce qui est perturbant, c'est que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    leBoutonTousParag.addEventListener('click', actionImporteTousParag)
    cible bien l'écouteur qui suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    actionImporteTousParag = function(e)
     {
      e.target.style.backgroundColor = 'rgba(255, 255, 0, 0.9)';
      console.log('Importer tous les paragraphes du modèle ');
     },
    ...
    La preuve, si je le renomme, par exemple, actionImporteAutreChose,
    j'ai une Reference Error en console : actionImporteTousParag is not defined

    J'en déduis donc que l'événement click est bien associé au bouton leBoutonTousParag, et devrait invoquer actionImporteTousParag
    Par contre, cliquer sur le bouton ne provoque rien.
    Normalement, je devrais recevoir un message en console, et le bouton devrait adopter un fond jaune.
    J'ai essayé avec d'autres événements : blur, focus, mouseleave, ...
    Rien n'y fait.
    Comme quand j'invoquais divImport.innerHTML = ...
    Le remplacement par divImport.insertAdjacentHTML n'a rien changé, la simplification de mon identifiant CSS non plus.

    C'est vraiment comme si un nouveau bouton, ajouté par Ajax, était voué à être sourd.

    Aurais-tu une autre idée à me proposer ?
    Merci,
    Christian.

  5. #5
    Expert confirmé
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 102
    Par défaut
    Normalement ça devrait marcher. Mais comment est déclarée divImport ? Où est-elle insérée dans le DOM ?
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  6. #6
    Expert confirmé Avatar de Toufik83
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2012
    Messages
    2 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Maroc

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2012
    Messages : 2 534
    Par défaut
    Si je comprends bien, tu veux attacher l'événement clique à un élément ajouté dynamiquement avec ajax.

    pour cela, il faut passer par délégation (choisir un parent statique "body par exemple" qui existe dans le DOM), et attacher l'événement clic à ses enfants en dehors d'ajax.

    Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    //ta fonction ajax
    $.ajax({....});
    //attacher l'événement clic à l'élément "#tous" par délegation
    $('body').on('click','#tous', function(e){
         console.log("vous avez cliqué sur "+$(this).attr('id'));
    });
    Tu peux mettre un autre parent statique au lieu de body.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Interagir sur élément crée dynamiquement
    Par derzy971 dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 16/03/2010, 09h00
  2. Réponses: 5
    Dernier message: 05/09/2008, 18h17
  3. GetFields sur textBox crées dynamiquement
    Par Cabire dans le forum Windows Forms
    Réponses: 14
    Dernier message: 22/02/2008, 11h14
  4. [VBA-W] Evénement sur bouton créé dynamiquement
    Par ptilo dans le forum VBA Word
    Réponses: 9
    Dernier message: 21/06/2006, 12h08
  5. Réponses: 5
    Dernier message: 06/03/2006, 18h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo