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

  1. #1
    Membre du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    mars 2016
    Messages
    40
    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 : 40
    Points : 47
    Points
    47

    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 du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    mars 2016
    Messages
    40
    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 : 40
    Points : 47
    Points
    47

    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 éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    juin 2010
    Messages
    2 988
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : juin 2010
    Messages : 2 988
    Points : 6 477
    Points
    6 477

    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 du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    mars 2016
    Messages
    40
    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 : 40
    Points : 47
    Points
    47

    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 éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    juin 2010
    Messages
    2 988
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : juin 2010
    Messages : 2 988
    Points : 6 477
    Points
    6 477

    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
    Membre expérimenté Avatar de Toufik83
    Homme Profil pro
    Développeur informatique
    Inscrit en
    janvier 2012
    Messages
    1 013
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Maroc

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

    Informations forums :
    Inscription : janvier 2012
    Messages : 1 013
    Points : 1 651
    Points
    1 651

    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.

  7. #7
    Membre du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    mars 2016
    Messages
    40
    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 : 40
    Points : 47
    Points
    47

    Par défaut J'ai bien mon élément, certaines propriétés fonctionnent, mais pas toutes

    J'ai vraiment l'impression que Javascript exécute certaines de mes instructions, et en ignore d'autres.
    Tout d'abord, une certitude :
    Mon bouton est bien reconnu par le DOM, et capté par querySelector.
    Avec un identifiant lourd, ou simplifié à #tous, comme ci-dessous, dans l'écouteur Ajax. Ca n'a rien changé
    La preuve, c'est que :
    1. Lorsque je lui donne une ombre bleue, je la vois
    2. Lorsque je demande sa valeur en console, je vois Tous


    Par contre, le addEventListener('click', ...) est superbement ignoré
    Mais, si je cible une fonction qui n'existe pas: erreur en console.

    Si je veux lui mettre une ombre verte, une seconde plus tard, elle reste bleue.
    Mais si je lui demande la couleur de l'ombre, la console affiche la nouvelle valeur "rgba(0, 255, 0, 0.9) 0.5vmin 0.5vmin 1vmin"
    C'est à dire celle d'une ombre verte.
    Ne serait-ce pas un problème de bind(this) ?

    Pour lier les événements au niveau du plus proche parent statique, je ne sais pas comment faire, sans JQuery, que je n'utilise pas.
    Je n'ai jamais voulu d'élément extérieur dans mes programmes.
    Mais de toutes façons, le problème n'est pas là, je crois.
    Puisque le querySelector me rend bien mon élément, importé ultérieurement par Ajax.
    Le problème n'est pas de le cibler dans une variable, mais de lui ajouter un événement.

    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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
     
     actionImporteTousParag = function(e)
     {
      e.target.style.backgroundColor = 'rgba(255, 255, 0, 0.9)';
      console.log('Importer tous les paragraphes du modèle ');
      /*xhrImporte.open('POST', 'ajax/ajaxModParagImporte.php');
      xhrImporte.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      xhrImporte.send('prest=' + prestId + '&modele=' + modeleId);
      e.target.style.visibility = 'hidden';*/
     },
     // *** Ajax event handlers ***
     actionXhrModele = function(e)
     {
      if(e.target.readyState == 4)
      {
       if(e.target.status == 200)
       {
        divImport.innerHTML = e.target.responseText;
        const leBoutonTousParag = divImport.querySelector('#tous');
        leBoutonTousParag.addEventListener('click', actionImporteTousParag);
        leBoutonTousParag.style.boxShadow = 'rgba(0, 0, 255, 0.9) 0.5vmin 0.5vmin 1vmin'; // OK, ombre bleue
        console.log(leBoutonTousParag.value); // OK, il affiche "Tous"
        console.log(leBoutonTousParag.onclick); // null
        setTimeout
        (
         (ceci) =>
         {
          ceci.style.boxShadow = 'rgba(0, 255, 0, 0.9) 0.5vmin 0.5vmin 1vmin'; // Ne change pas le bouton, qui reste bleu. Aucune ombre verte
          console.log(ceci.style.boxShadow); // Ici aussi, la console affiche bien rgba(0, 255, 0, 0.9) 0.5vmin 0.5vmin 1vmin
         },
         1000,
         leBoutonTousParag
        );
        //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>';
      }
     },
    N'y aurait-il pas une deuxième variable cachée, qui reçoit l'event listener, l'ombre verte ?
    J'essaye de comprendre la logique de ce comportement.
    L'instruction d'ombre bleue fonctionne.
    Mais pas la verte, une seconde plus tard.
    Ni l'event listener.

    En principe, dès que j'ai mon élément dans une variable, je devrais pouvoir tout faire avec.
    Mais je voudrais vraiment y arriver, plutôt que de passer par une solution sale, telle que de remplacer mes requêtes Ajax par des liens qui chargent une nouvelle page dans un <IFRAME>
    Page qui saura néanmoins gérer ses événements, puisqu'instaurés au chargement.

  8. #8
    Membre expérimenté Avatar de Toufik83
    Homme Profil pro
    Développeur informatique
    Inscrit en
    janvier 2012
    Messages
    1 013
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Maroc

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

    Informations forums :
    Inscription : janvier 2012
    Messages : 1 013
    Points : 1 651
    Points
    1 651

    Par défaut

    Apparemment c'est le mot clé "const" qui pose problème, il faut déclarer encore une fois la variable 'leBoutonTousParag ' dans setTimeout.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    setTimeout(function(){
           //"let" ou "var" ou "const"
    	const leBoutonTousParag = divImport.querySelector('#tous');//obligatoire
    	leBoutonTousParag.style.boxShadow = 'rgba(0, 255, 0, 0.9) 0.5vmin 0.5vmin 1vmin';
    },1000);

  9. #9
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    juin 2010
    Messages
    2 988
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : juin 2010
    Messages : 2 988
    Points : 6 477
    Points
    6 477

    Par défaut

    Attention La dernière instruction de ta fonction pose problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    divImport.innerHTML += '<P style="text-align:center;">' + (Date.now() - delai) + ' ms</P>';
    Utiliser += avec innerHTML cause une corruption du DOM, comme si l’élément était d’abord entièrement vidé, puis re-rempli avec une copie de son contenu précédent, plus le nouveau contenu. Tout se passe comme si tu avais écrit l’instruction comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    divImport.innerHTML = divImport.innerHTML + ... ;
    Dans la plupart des cas, cette re-création du contenu ne pose pas de problème… Sauf quand il y a des gestionnaires d’évènements attachés aux nœuds enfants. Dans ce cas, les gestionnaires d’évènements sont perdus !

    Et il n’y a pas que ça qui est perdu : lors de l’appel à setTimeout, la variable leBoutonTousParag fait référence à l’ancien bouton qui était enfant de divImport avant la corrution. La référence existe toujours parce que ta fonction (ceci) => { ... } a crée une closure qui la contient. Mais quand tu modifies ce bouton, tu modifies un élément qui n’est plus rattaché à rien, et celui qui est actuellement visible n’est pas modifié.

    Pour cette raison je te conseille d’abandonner définitivement innerHTML qui cause cet effet de bord sournois, et d’utiliser à la place insertAdjacentHTML().
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  10. #10
    Membre du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    mars 2016
    Messages
    40
    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 : 40
    Points : 47
    Points
    47

    Par défaut Super ! Ca marche.

    Merci Toufik83 et Watilin.
    Ayant lu vos réponses, j'ai bien compris qu'écraser le DOM avec innerHTML n'était pas une bonne solution.
    Aussi, j'ai décidé de m'y reprendre autrement, proprement, en ajoutant des éléments fils dans un élément père.

    Pour ce faire, je crée, statiquement, une table dont le tbody est vide

    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
      <DIV class="scrollable">
       <TABLE class="claire">
        <THEAD>
        <TR>
         <TH>Paragraphe</TH>
         <TH style="width:10vmin;">Action</TH>
        </TR>
       </THEAD>
       <TBODY>
       </TBODY>
      </TABLE>
     </DIV>

    Ensuite, je cible ce TBODY dans mon JavaScript

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    const
    ...
    bodyImport = document.querySelector('article#artiImport div.scrollable table tbody'),
    ...
    ;
    D'autre part, ma requête Ajax ne me délivre plus une <TABLE> HTML,
    mais un tableau JSON de valeurs à insérer dans le tbody
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
     ...
     $lesParag = $leModele->getParag();
     $reponse = ['modeleId' => array($leModele->getId(), NULL)]; // Première ligne, pour l'identifiant du modèle
     foreach($lesParag as $leParag)
     {
      $reponse[$leParag['ordre']] = array($leParag['id'], champsValeurs($leParag['texte'], $lesChamps)); // Ensuite, une ligne par paragraphe
     }
     echo(json_encode($reponse));
    En JavaScript, dans mon écouteur Ajax, le transforme la réponse JSON en un tableau,
    avec lequel, j'ajoute, en boucle, des éléments HTML dans mon TBody vide.

    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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
     
     actionXhrModele = function(e)
     {
      var tableauJson, laTr, laTd, leBouton;
      if(e.target.readyState == 4)
      {
       if(e.target.status == 200)
       {
        if(e.target.responseText.length > 10)
        {
         // Ici, on ajoute proprement des éléments au DOM
         tableauJson = JSON.parse(e.target.responseText);
         //console.table(tableauJson);
     
         if(parseInt(tableauJson['modeleId']) > 0)
         {
          bodyImport.innerHTML = '';
          leBoutonTousParag.dataset.modele = tableauJson['modeleId'];
          leBoutonTousParag.style.visibility = 'visible';
          //console.log('Taille du tableau ' + tableauJson.length);
          for(var i in tableauJson)
          {
           if(tableauJson[i][1] != null) // Pour ne pas prendre la ligne zéro, qui définit le modeleId
           {
            leBouton = document.createElement('input');
            leBouton.value = '->';
            leBouton.type = 'button';
            leBouton.dataset.parag = parseInt(tableauJson[i][0]);
            leBouton.addEventListener('click', actionImporteParag);
     
            laTr = document.createElement('tr');
            laCase = document.createElement('td');
            laCase.innerHTML = tableauJson[i][1];
            //console.log(tableauJson[i][1]);
            laTr.appendChild(laCase);
     
            laCase = document.createElement('td');
            laCase.style.textAlign = 'center';
            laCase.appendChild(leBouton);
            laTr.appendChild(laCase);
     
            bodyImport.appendChild(laTr);
           }
          }
         }
         else
         {
          // En cas d'erreur, on peut écraser le DOM. Aucun événement à gérer.
          paragTiming.innerHTML = 'Aucun num&eacute;ro de mod&eacute;le';
          paragTiming.style.backgroundColor = 'background-color:rgba(255, 0, 0, 0.6)';
         }
        }
        else
        {
         paragTiming.innerHTML = 'Aucune r&eacute;ponse ajax';
         paragTiming.style.backgroundColor = 'background-color:rgba(255, 0, 0, 0.6)';
        }
       }
       else
       {
        paragTiming.innerHTML = 'Echec requ&ecirc;te ajax';
        paragTiming.style.backgroundColor = 'rgba(255, 0, 0, 0.6);';
       }
       paragTiming.style.backgroundColor = 'initial';
       paragTiming.innerHTML = (Date.now() - delai) + ' ms';
      }
     },
    ...
    Et voici les événements invoqués par les boutons :
    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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
     actionImporteTousParag = function(e)
     {
      const modeleId = parseInt(e.target.dataset.modele);
      if(modeleId > 0)
      {
       console.log('Importer tous les paragraphes du modèle ' + modeleId);
       /*xhrImporte.open('POST', 'ajax/ajaxModParagImporte.php');
       xhrImporte.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
       xhrImporte.send('prest=' + prestId + '&modele=' + modeleId);
       e.target.style.visibility = 'hidden';*/
      }
      else
      {
       paragTiming.innerHTML = 'Aucun mod&egrave;le';
       paragTiming.style.backgroundColor = 'background-color:rgba(255, 0, 0, 0.6)';
      }
     },
     actionImporteParag = function(e)
     {
      const paragId = parseInt(e.target.dataset.parag);
      if(paragId > 0)
      {
       e.target.style.backgroundColor = 'rgba(255, 255, 0, 0.7)';
       console.info('Importe le paragraphe ' + paragId);
      }
      else
      {
       console.error('Paragraphe inconnu');
      }
      /*xhrImporte.open('POST', 'ajax/ajaxModParagImporte.php');
      xhrImporte.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      xhrImporte.send('prestId=' + prestId + '&parag=' + paragId);
      e.target.style.visibility = 'hidden';*/
     },
    D'abord, je me contente d'un message en console, ou d'un changement de couleur du bouton,
    pour vérifier si l'événement déclenche correctement.
    On programmera la requête Ajax après.

    Et ça marche !
    Mon message apparaît bien en console, avec le bon paramètre, récupéré des micro-données (dataset).
    Désormais, je n'utiliserai plus l'innerHTML que pour du code simple, non-événementiel, ou pour vider un élément de son contenu.
    Ca va plus vite que d'effacer tous les enfants d'un élément, un par un.
    Comme je le fais pour vider le TBody d'un éventuel modèle précédent en entrée de procédure.

    Merci Watilin et Toufik83
    Christian.

  11. #11
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    juin 2010
    Messages
    2 988
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : juin 2010
    Messages : 2 988
    Points : 6 477
    Points
    6 477

    Par défaut

    Quelques petites remarques :

    tu peux simplifier ta gestion du retour ajax en utilisant onload à la place de onreadystatechange.

    Les sélecteurs CSS sont plus efficaces quand ils sont plus courts. La sélection fonctionne de droite à gauche : dans table tbody, le moteur de sélection va d’abord chercher tous les éléments tbody, puis vérifier ceux qui ont un parent table. Dans un cas normal, les tbody ont toujours un parent table, donc tu peux supprimer la partie table.
    De plus, un sélecteur d’id est plus efficace qu’un sélecteur de balise, et ainsi #artiImport seul est plus efficace que article#artiImport.
    Pour la partie intermédiaire div.scrollable, garde-la seulement si elle permet d’éliminer des tbody qui ne sont pas enfants de .scrollable. Dans tous les cas, tu peux sans doute retirer au moins la partie div.

    Jette un œil aux options de json_encode, en particulier JSON_HEX_QUOT et JSON_HEX_APOS.

    La boucle for ... in est dépréciée, pour des raisons pas forcément évidentes à expliquer aujourd’hui (maintenant que plus personne n’utilise le framework Prototype.js), mais il y a plusieurs alternatives, tu n’as que l’embarras du choix :


    Ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    paragTiming.style.backgroundColor = 'background-color:rgba(255, 0, 0, 0.6)';
    Supprime la partie background-color: dans le CSS, sinon la syntaxe sera incomprise.

    Pour vider un élément de son contenu, tu peux aussi utiliser .textContent.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

+ 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, 08h00
  2. Réponses: 5
    Dernier message: 05/09/2008, 17h17
  3. GetFields sur textBox crées dynamiquement
    Par Cabire dans le forum Windows Forms
    Réponses: 14
    Dernier message: 22/02/2008, 10h14
  4. [VBA-W] Evénement sur bouton créé dynamiquement
    Par ptilo dans le forum VBA Word
    Réponses: 9
    Dernier message: 21/06/2006, 11h08
  5. Réponses: 5
    Dernier message: 06/03/2006, 17h38

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