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

JavaScript Discussion :

Refactorisation de code


Sujet :

JavaScript

  1. #1
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut Refactorisation de code
    Bonjour à toutes et tous,

    Je me permets de poster un topic car je m'arrache depuis plusieurs jours le peu de cheveux que j'ai sur la tête sur une re-factorisation de code pour une petite app.

    Voici l'idée: il s'agit d'une simple page web type "arbre de décision". J'ai deux sections qui resteront visibles, les réponses sont dans des sections masquées en display: none;, en fonction du choix de réponse de l'utilisateur elles s'afficheront avec une petite animation CSS issue d'une librairie tierce.

    J'ai donc créer mon squelette HTML comme suit:

    Code html : 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
    <header>
          <h1>
            Mon super titre de la mort qui tue
          </h1>
        </header>
        <main>
          <section id="init"><!-- Cette section restera toujours visible -->
            <p>
              <i>Icône Font Awesome pour faire joli</i>
              Ma thématique
            </p>
          </section>
          <section id="question_1" class="is_active"><!-- Cette section restera aussi toujours visible -->
            <p>
              <i>Oui ç fait joli </i>
              Ma première question
            </p>
            <a class="btn btn-outline-success" id="step_1" href="#next_1">Oui</a><!-- Lien avec une classe Bootstrap pour styliser en bouton -->
            <a class="btn btn-outline-danger" id="no_1" href="#next_no_1">Non</a>
          </section>
     
    <section id="next_1" class="animate__animated"><!-- Classe pour la librairie animate.css afin donner un effet -->
            <p>
              <i>Encore une icône</i>
              Ma deuxième question
            </p>
            <a id="step_2" href="#next_2" class="btn btn-outline-success">Oui</a>
            <a id="no_2" href="#next_no_2" class="btn btn-outline-danger">Non</a>
          </section>

    Les sections de mon arbre se succèdent toutes avec le même format d'id et classe CSS, mais à part les deux sections commentées qui restent visibles, les autres sont masquées et apparaissent en fonction du choix de l'utilisateur, avec le petit effet d'animate.css.

    J'ai réussi à le faire fonctionner mais en codant en dur/procédural/pas du tout optimisé mon JS. Je mettait bien l'écouteur sur le parent du lien, et en allant chercher l'id des sections, j'arrivais bien à toggle la classe CSS (je souhaite bien utiliser le toggle), en pensant de cette manière:

    Au click du lien
    - la section correspondant au href cliqué toggle la classe CSS
    - j'ajoute ma classe CSS de la librairie animate (qui est "animate__bounceInDown", je ne peux pas la mettre dans le HTML lorsque je le fais l'animation ne fonctionne pas)
    - j'enlève la classe CSS "is_active" de la section cloquée
    - j'ajoute la classe CSS "is_active" à la section qui apparait


    Je sollicite votre aide car j'ai réussi à coder et avoir le résultat souhaité en répétant mon code encore et encore pour chaque choix possible. Depuis plusieurs jours, je cherche à faire une boucle qui serait beaucoup plus propre et maintenable. Je pense qu'a force d'être dessus mon esprit s'embrouille, je vois pas sur quoi je dois boucler et ça m'énerve voilà pourquoi je me permets de poster ce sujet.

    Je suis ouvert à toutes propositions, y compris ne pas mettre de JS et utiliser du CSS, tant que le résultat est le même.

    Par avance un immense merci aux personnes qui prendront le temps de m'aider.

    Bonne fin de semaine, ou week-end

  2. #2
    Inactif  
    Homme Profil pro
    Webmaster
    Inscrit en
    Juin 2021
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juin 2021
    Messages : 645
    Points : 1 280
    Points
    1 280
    Par défaut
    Bonjour,

    Commence par montrer ton code JS.

    Normalement, tu devrais pouvoir y déceler les redondances.

  3. #3
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut
    Salut,

    Merci pour ta réponse.

    Voici le code JS que j'utilise et duplique pour chaque lien:

    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
     
     
    // je mets tout dans des variables
     
    const stepOne = document.getElementById("step_1");
    const stepOneSection = document.getElementById("next_1");
    const mainSection = document.getElementById("question_1");
     
    // ecouteur d'evenement
     
    stepOne.addEventListener("click", (e) => {
      e.preventDefault();
      stepOneSection.classList.toggle("is_hidden");
      stepOneSection.classList.add("animate__bounceInDown");
      mainSection.classList.remove("is_active");
      stepOneSection.classList.add("is_active");
    });
    Je duplique ces lignes en changeant les ID des variables, et les noms aussi. Ca fonctionne mais le code se répète pour chaque possibilité.

    Est-ce que tu vois où se situe le souci?

    Par avance merci pour ton temps.

  4. #4
    Inactif  
    Homme Profil pro
    Webmaster
    Inscrit en
    Juin 2021
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juin 2021
    Messages : 645
    Points : 1 280
    Points
    1 280
    Par défaut
    On voit déjà une incohérence dans le code HTML :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	<section id="next_1" class="...">
            <p>
              <i>...</i>
              Ma deuxième question
    1- LOGIQUEMENT, ça devrait être :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    	<section id="question_2"...

    2- Ensuite, si on a des traitement équivalents à faire sur plusieurs éléments, on leur ajoute une classe :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    	<section id="question_1" class="question ...">
    ...
    	</section>
    	<section id="question_2" class="question ...">
    ...
    	</section>
    ...
    on peut alors TOUS les cibler en JS avec document.querySelectorAll :
    Code js : Sélectionner tout - Visualiser dans une fenêtre à part
    const questions = document.querySelectorAll( '.question' ); // à noter : le . de la classe
    On peut ensuite les parcourir avec .forEach() :
    Code js : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    questions.forEach(function( question ) {
       ......
    });
    Voilà pour le début.

  5. #5
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut
    Super merci pour ta rapidité de réponse!

    J'essaye ça cette après-midi, et je reviens t'indiquer si tout st OK ou non.

    Merci encore!

    Bon appétit.

  6. #6
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut
    Salut,

    Je n'ai pas pu poster ma réponse avant, mes excuses.

    Alors j'ai renommé toutes mes sections avec la classe indiquée, et essayé ta boucle, en console.log chaque "question" j'ai bien toute la section.

    Ca fait un bail que j'ai pas codé en Js du coup je galère un peu... maintenant je n'arrive plus à faire afficher la bonne section au click...

    Je vais persévérer, mais si tu as une piste je suis preneur

    Merci en tout cas pour ton aide, je reviendrais dans la semaine vous dire si je suis toujours bloqué ou non.

    Bonne fin de journée.

  7. #7
    Inactif  
    Homme Profil pro
    Webmaster
    Inscrit en
    Juin 2021
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juin 2021
    Messages : 645
    Points : 1 280
    Points
    1 280
    Par défaut
    bonjour,

    là encore, si tu ne montres pas ton code, on ne peut rien en dire.

    Le minimum est de savoir quels sont les critères / conditions pour ouvrir la prochaine section.

    Si les réponses sont toujours oui / non, on peut aussi envisager des cases à cocher (checkbox ou boutons radio).
    D'autant que, normalement, le but d'un quizz / sondage est d'envoyer ou enregistrer les réponses !

    On trouve aussi des quizz JS sur le web :

  8. #8
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut
    Salut,

    Désolé de ne pas avoir parlé plus tôt, je profitais de quelques jours de vacances bien méritées.

    Je te montre intégralement mon projet non fini. Comme indiqué, je sors d'un gros hiatus de coding, du coup j'ai perdu beaucoup mes excuses si c'est simple pour vous...

    Voici un gif de ce que j'aimerai réaliser:




    Pour faire ça, j'y suis allé en "forçant" et en copiant/collant le code, en changeant juste les cibles des variables. Voici le code que je répètais, mes excuses c'est dégueu :

    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
     
    const premier = document.getElementById("step-1");
    const stepOneSection = document.getElementById("step-2");
    const mainSection = document.getElementById("question___1");
     
    const premierNon = document.getElementById("step-1_no");
    const successOne = document.getElementById("end-success_1");
     
    premier.addEventListener("click", (e) => {
      e.preventDefault();
      stepOneSection.classList.toggle("is_hidden");
      stepOneSection.classList.add("animate__bounceInDown");
      mainSection.classList.remove("is_active");
      stepOneSection.classList.add("is_active");
    });
     
    premierNon.addEventListener("click", (e) => {
      e.preventDefault();
      successOne.classList.toggle("is_hidden");
      successOne.classList.add("animate__bounceInDown");
      mainSection.classList.toggle("is_active");
      successOne.classList.add("is_active");
    });
    Ce code je le répétais pour chaque section/lien cliqué. Depuis, j'ai posté sur ce forum, j'ai renommé mes sections, renommé les liens et mis la boucle que tu m'as indiqué initialement.

    Pour tout t'avouer, j'arrive pas a target les liens Lorsque j'effectue ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    const questions = document.querySelectorAll(".question");
     
    questions.forEach(function (question) {
      question.addEventListener("click", function (e) {
        e.preventDefault();
        console.log(question)    
      })
    });
    J'ai bien la section cliquée en console, mais je n'arrive pas à prendre uniquement le lien. Dans mon idée, je vais écrire les commentaires de ce que je pense dis-moi stp si je suis sur la bonne voie:

    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
     
    const questions = document.querySelectorAll(".question");
     
    questions.forEach(function (question) {
      question.addEventListener("click", function (e) {
        e.preventDefault();
     
        /* 
        Si le lien de la section cliquée correspond à un oui
          Enleve la classe "is_hidden" à la section cible
          Ajoute la classe "active" a la section cible
          Ajoute la classe d'animate.css a la section cible
          Enleve la classe "active" de la section cliquée
        Sinon 
          Enleve la classe "is_hidden" à la section cible qui correspond à "non_{numéroDeQuestion}"
          Ajoute la classe "active" a la section cible qui correspond à "non_{numéroDeQuestion}"
          Ajoute la classe d'animate.css a la section cible qui correspond à "non_{numéroDeQuestion}"
          Enleve la classe "active" de la section cliquée
          */
      })
    });
    Le souci c'est que j'arrive pas à prendre dynamiquement le href du lien cliqué, et donc enchaîner ma logique.

    J'espère avoir été davantage précis, je suis prêt à modifier pour des checkbox si tu penses que ça sera plus simple pour finaliser le projet

    Merci infiniment pour ton aide en tout cas!

    EDIT: C'est important je pense de préciser que cet arbre a juste besoin de modéliser une "marche à suivre". Rien n'est sauvegardé en base, il s'agit juste d'aider les utilisateurs en fonction du cas de figure dans le lequel ils sont, et les orienter vers les outils métiers selon le cas.

  9. #9
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut
    Re bonjour,

    J'ai un petit peu avancé, voici le JS:

    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
     
    const questions = document.querySelectorAll(".question");
     
    questions.forEach (function (question) {
     
      question.addEventListener('click', function (e) {
     
        e.preventDefault();
     
        this.classList.remove("is_active");
     
        let link = this.querySelector('a').getAttribute('href')
     
        console.log(this, link)
     
      })
    })
    Au click, le "this" correspond bien à ma section, ça enlève bien la classe "is_active", et la variable "link" prend bien le "#href" que j'ai indiqué dans mon HTML.

    Le souci que je rencontre désormais, c'est lorsque j'écris:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    link.classList.add('is_active')
    j'ai une erreur:

    Uncaught TypeError: Cannot read property 'classList' of undefined
    at HTMLElement.<anonymous>
    ça me semble logique mais là j'avoue bloquer...

    Tu aurais une idée?

    Merci encore je me sens nul mais ça revient petit à petit....

  10. #10
    Inactif  
    Homme Profil pro
    Webmaster
    Inscrit en
    Juin 2021
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juin 2021
    Messages : 645
    Points : 1 280
    Points
    1 280
    Par défaut
    Bonjour,

    je viens de bosser sur ta problématique.


    Code html : 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
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    <section id="init" class="animate__animated animate__bounceInDown"><!-- Cette section restera toujours visible -->
      <H4>arbre de décision</H4>
    </section>
     
    <section id="question_1" class="question is_active animate__animated animate__bounceInDown"><!-- Cette section restera aussi toujours visible -->
      <p>question 1</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="11">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="10">Non</button>
    </section>
     
    <section id="question_11" class="question">
      <p>question 2 - 11</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="111">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="110">Non</button>
    </section>
    <section id="question_10" class="question">
      <p>question 2 - 10</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="101">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="100">Non</button>
    </section>
     
    <section id="question_111" class="question">
      <p>question 3 - 111</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="1111">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="1110">Non</button>
    </section>
    <section id="question_110" class="question">
      <p>question 3 - 110</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="1101">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="1100">Non</button>
    </section>
    <section id="question_101" class="question">
      <p>question 3 - 101</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="1011">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="1010">Non</button>
    </section>
    <section id="question_100" class="question">
      <p>question 3 - 100</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="1001">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="1000">Non</button>
    </section>
     
    <section id="question_1111" class="question">
      <p>question 4 - 1111</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="11111">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="11110">Non</button>
    </section>
    <section id="question_1110" class="question">
      <p>question 4 - 1110</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="11101">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="11100">Non</button>
    </section>
    <section id="question_1101" class="question">
      <p>question 4 - 1101</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="11011">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="11010">Non</button>
    </section>
    <section id="question_1100" class="question">
      <p>question 4 - 1100</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="11001">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="11000">Non</button>
    </section>
    <section id="question_1011" class="question">
      <p>question 4 - 1011</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="10111">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="10110">Non</button>
    </section>
    <section id="question_1010" class="question">
      <p>question 4 - 1010</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="10101">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="10100">Non</button>
    </section>
    <section id="question_1001" class="question">
      <p>question 4 - 1001</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="10011">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="10010">Non</button>
    </section><section id="question_1000" class="question">
      <p>question 4 - 1000</p>
      <button type="button" class="btn-reponse btn btn-outline-success" data-next="10001">Oui</button>
      <button type="button" class="btn-reponse btn btn-outline-danger" data-next="10000">Non</button>
    </section>
    Code css : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    .question { margin:10px; padding:20px; border-raadius:10px; border:1px solid lightblue; }
    .question { display:none; }/*on masque*/
    .question.is_active { display:block; }/*on affiche*/
    Code js : 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
    const questions = document.querySelectorAll( '.question' ); // à noter : le . de la classe
    const reponses = document.querySelectorAll( '.btn-reponse' ); // à noter : le . de la classe
     
    reponses.forEach(function( reponse ) {
      reponse.addEventListener("click", function (e) {
        e.preventDefault();
        var next = this.dataset.next;
        // fermeture des questions suivantes
        close_higher_question_sections( next );
        // ouverture de la question concernee
        var next_section = document.querySelector('#question_'+next);
        next_section.classList.add("is_active");
        next_section.classList.add("animate__animated")
        next_section.classList.add("animate__bounceInDown");
      });
    });
    // fermeture des questions suivantes (si on clique sur une autre réponse précédente)
    function close_higher_question_sections( next )
    {
      var prev_num = Math.floor(Number(next/10)); // numero équivalent à celui de la section en cours
      questions.forEach(function( question ) {
        var question_num = Number(question.id.replace('question_',''));
        console.log( question_num );
        console.log( prev_num );
        if( question_num > prev_num )
        {
          question.classList.remove("is_active")
          question.classList.remove("animate__animated");
          question.classList.remove("animate__bounceInDown");
        }
      });
    }

    LE PRINCIPE : numérotation BINAIRE des sections
    • "Non" -> 0
    • "Oui" -> 1
    • mais on peut ajouter d'autres réponses : "Ne sait pas" -> 2

    Chaque <section> dépend de l'enchainement de réponses.
    • question 1 -> 1
    • question 2 -> 11 / 10
    • question 3 -> 111 /110 /101 /100
    • question 4 -> 1111 / 1110 / 1101 / 1100 / 1011 / 1010 / 1001 / 1000
    • ...

    ex.
    • réponse à la question 1 : oui -> next section = 11
    • réponse à la question 2 : oui -> next section = 111
    • réponse à la question 3 : non -> next section = 1110
    • réponse à la question 4 : non -> next section = 11100
    • ...

    Ainsi, TOUS les index sont différents, donc UNIQUES.
    On peut donc écrire <section id="question_xxxxx" ...> où xxxxx est cet index.

    Par cette méthode, il est aussi facile de "retracer" les choix effectués.
    ex. : 11100 (correspond à l'exemple précédent)
    • Réponses à chacune des questions : 1100 -> oui oui non non



    Explication de la fonction function close_higher_question_sections( next ) :
    • Si on a déjà répondu à plusieurs questions (cf exemple précédent)
    • et qu'on change de réponse (ex. réponse à la question 2 : non au lieu de oui)
      -> il faut alors refermer les sections "suivantes" supérieures (ici : question 3 et 4)

    var prev_num = Math.floor(Number(next/10));Car chaque question suivante a un index 10 fois supérieur à la question précédente :

  11. #11
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut
    Bonjour,

    Que dire, à part que c'est parfait et pile ce que je voulais voire même mieux (j'avais pas prévu le cas d'erreur, toi si… énorme!!)!!!!!

    T'es trop fort, je sais pas comment te remercier!!

    J'avais pas pensé à mettre des boutons plutôt que des liens, et utiliser le data…

    Un merci infini, tu imagines pas quelle épine tu m'ôtes du pied!!!!

  12. #12
    Inactif  
    Homme Profil pro
    Webmaster
    Inscrit en
    Juin 2021
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juin 2021
    Messages : 645
    Points : 1 280
    Points
    1 280
    Par défaut
    J'ai amélioré mon script, en gardant les boutons cliqués "actifs" :
    • btn-outline-success -> btn-success
    • btn-outline-danger -> btn-danger


    Code js : 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
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    window.addEventListener("DOMContentLoaded", (event) => { // on attend que le DOM soit chargé
      "use strict"; // mode strict
     
      const questions = document.querySelectorAll( '.question' ); // à noter : le . de la classe
      const reponses = document.querySelectorAll( '.btn-reponse' ); // à noter : le . de la classe
     
      reponses.forEach(function( reponse ) {
        reponse.addEventListener("click", function (e) {
          e.preventDefault();
          var next = this.dataset.next; // index de la section suivante
          // ---------
          // fermeture des questions suivantes
          close_higher_question_sections( next );
          // ---------
          // ouverture de la question concernee
          var next_section = document.querySelector('#question_'+next);
          if( next_section )
          {
            next_section.classList.add("is_active");
            next_section.classList.add("animate__animated")
            next_section.classList.add("animate__bounceInDown");
          }
          // ---------
          // buttons : non actifs (de cette section)
          var children = this.parentNode.children;
          for (var i = 0; i < children.length; i++) 
          {
            if( children[i].classList.contains('btn-success') ){
              children[i].classList.add('btn-outline-success');
              children[i].classList.remove('btn-success');
            }
            if( children[i].classList.contains('btn-danger') ){
              children[i].classList.add('btn-outline-danger');
              children[i].classList.remove('btn-danger');
            }
          }
          // ---------
          // CE button : ACTIF
          if( this.classList.contains('btn-outline-success') ){
            this.classList.remove('btn-outline-success');
            this.classList.add('btn-success');
          }
          if( this.classList.contains('btn-outline-danger') ){
            this.classList.remove('btn-outline-danger');
            this.classList.add('btn-danger');
          }
          // ---------
        });
      });
      // fermeture des questions suivantes (si on clique sur une autre réponse précédente)
      function close_higher_question_sections( next )
      {
        var prev_num = Math.floor(Number(next/10));
        questions.forEach(function( question ) {
          var question_num = Number(question.id.replace('question_',''));
          // ---------
          if( question_num > prev_num )
          {
            question.classList.remove("is_active")
            question.classList.remove("animate__animated");
            question.classList.remove("animate__bounceInDown");
            // -------
            // buttons : non actifs
            var children = question.children;
            for (var i = 0; i < children.length; i++) 
            {
              if( children[i].classList.contains('btn-success') ){
                children[i].classList.add('btn-outline-success');
                children[i].classList.remove('btn-success');
              }
              if( children[i].classList.contains('btn-danger') ){
                children[i].classList.add('btn-outline-danger');
                children[i].classList.remove('btn-danger');
              }
            }
            // -------
          }
        });
      }
     
    });
    VOIR AUSSI :

  13. #13
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    16 946
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 16 946
    Points : 44 086
    Points
    44 086
    Par défaut Cela me rappelle quelque chose
    Bonjour,

    La création d’un « arbre de décision » est, pour moi, une bonne raison de créer le code HTML dynamiquement.

    Dans un même ordre d’idée j’ai fait une réponse, sans suite, dans cette discussion : Structure d'une aventure textuelle en AS3, le principe reste le même.

    L’intérêt pour moi est que :
    • cela évite l’écriture de code HTML redondant ;
    • l’on dissocie les données du HTML ;
    • cela facilite la modification/maintenance.


    On utilise un objet de données, par exemple :
    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
     const DATAS = {
      "depart": {
        "titre": "Arbre de décision",
        "text": "Sélectionner votre contexte",
        "action": [{
            "label": "Contexte #1",
            "goto": "ctx1"
          },
          {
            "label": "Contexte #2",
            "goto": "ctx2"
          }
        ]
      },
      "ctx1": {
        "text": "C'est parti ...<br> faites votre choix",
        "action": [{
            "label": "choix #1",
            "goto": "ctx1_c1"
          },
          {
            "label": "choix #2",
            "goto": "ctx1_c2"
          },
          {
            "label": "choix #3",
            "goto": "ctx1_c3"
          }
        ]
      }, 
    // etc
    On peut ajouter tout ce que l’on veut comme information pour enrichir la présentation, à chaque étape correspond un lien, ou non, vers l’étape suivante décrite dans ce même objet.

    Dans cet objet on place toutes les données à exploiter, à partir de ces données il suffit de construire chaque « box étape » et d’affecter aux choix possibles un lien vers les données qui lui sont liées.
    A chaque action donc on crée la « boîte » contenant les infos liées, celle-ci pouvant même être réutilisée.

    Dans la fonction de construction on peut soit construire le HTML de façon verbeuse soit utiliser des « templates ». L’affectation des actions sur les choix peut ce faire dans celle-ci.

    Exemple complet :
    Code html : 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
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    <!DOCTYPE html>
    <html lang="fr">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Arbre de décision dynamique</title>
    <meta name="Author" content="NoSmoking">
    <meta name="DVP-discussion" content="d2116259">
    <meta name="description" content="Réalisation d'un arbre de décision dynamique construit à la volée.">
    <style>
    html, body {margin: 0;padding: 0;font: 1em/1.5 Verdana,sans-serif;}
    h1, h2, h3 {margin: .25em 0;color: #069;}
    time {float: right;margin: .5em;font-size: 0.9em;color: #888;}
    main {display: block;margin: auto;max-width: 60em;}
    section {position: relative;margin: 0 2em 1em; padding: 1px;}
    #arbre-decision {font-size: .9em;}
    #arbre-decision section {top: 0;padding: .5em;border: 1px solid #ABC;background-color: #FEFEFE;transition: all .5s;transition-timing-function: cubic-bezier(0,0,.1,1.25);}
    #arbre-decision section.animated {top: -100vh;}
    #arbre-decision span {position: absolute;top: .5em;right: .5em;font-style: italic;color: #BBB;}
    .btn-next {display: flex;justify-content: center;margin-bottom: 0;}
    .btn-next button {display: block;margin: .5em 1em;padding: .25em 1em;border: 1px solid #888;font: inherit;line-height: 2;background: #FFF;cursor: pointer;}
    .btn-next button:first-letter {text-transform: uppercase;}
    .btn-next .actif-btn {border: 1px solid #080;outline: 3px solid #0C0;outline-offset: -4px;background-color: #F0FFF0;}
    </style>
    </head>
    <body>
    <main>
      <header>
        <time datetime="2021-09-08">Sept. 2021</time>
        <h1>Création arbre de décision dynamique</h1>
      </header>
      <section id="arbre-decision"></section>
      <footer>
        <p>Voir la <a href="https://www.developpez.net/forums/showthread.php?t=2116259">discussion sur Developpez.com</a>
      </footer>
    </main>
    <script>
    "use strict";
    const DATAS = {
      "depart": {
        "titre": "Arbre de décision",
        "text": "Sélectionner votre contexte",
        "action": [{
            "label": "Contexte #1",
            "goto": "ctx1"
          },
          {
            "label": "Contexte #2",
            "goto": "ctx2"
          }
        ]
      },
      "ctx1": {
        "text": "C'est parti ...<br> faites votre choix",
        "action": [{
            "label": "choix #1",
            "goto": "ctx1_c1"
          },
          {
            "label": "choix #2",
            "goto": "ctx1_c2"
          },
          {
            "label": "choix #3",
            "goto": "ctx1_c3"
          }
        ]
      },
      "ctx2": {
        "text": "Pas plus d'info !",
        "action": [{
          "label": "Merci d'être venu !",
          "goto": "the_end"
        }]
      },
      "ctx1_c1": {
        "text": "Choix #1<br> faites votre super choix",
        "action": [{
            "label": "super choix #1",
            "goto": "ctx1_c1_c1"
          },
          {
            "label": "super choix #2",
            "goto": "ctx1_c1_c2"
          }
        ]
      },
      "ctx1_c2": {
        "text": "Choix #2<br> faites votre super choix",
        "action": [{
            "label": "super choix #1",
            "goto": "etape_05"
          },
          {
            "label": "super choix #2",
            "goto": "ctx1_c1_c2"
          }
        ]
      },
      "ctx1_c3": {
        "text": "Mauvais choix, à bientôt !",
        "action": []
      },
      "ctx1_c1_c1": {
        "text": "Votre choix final est on ne peut plus judicieux !",
        "action": [{
          "label": "Merci d'être venu !",
          "goto": "the_end"
        }]
      },
      "ctx1_c1_c2": {
        "text": "J'ai rien compris !",
        "action": [{
          "label": "Bye !",
          "goto": "the_end"
        }]
      },
      "the_end": {
        "titre": "The end !",
        "text": "A bientôt !",
        "action": []
      }
    };
    /**
     * Supprime les éléments qui ne sont plus concernés
     * et affecte la classe acive à l'élément
     * @param {HTMLElement} btn - le bouton cliqué
     */
    function removeNextEtape(btn) {
      const parent = btn.closest("section");
      const btnCde = parent.querySelector(".actif-btn");
      btnCde?.classList.remove("actif-btn");
      while (parent.nextElementSibling) {
        parent.nextElementSibling.remove();
      }
    }
    /**
     * function unique de création d'étape
     * @param {string} ref - étiquette de l'objet action
     */
    function createNextEtape(ref) {
      const oDest = document.getElementById("arbre-decision");
      if (DATAS[ref]) {
        // création conteneur
        const oSection = document.createElement("SECTION");
        // pour animation éventuelle
        oSection.classList.add("animated");
     
        // info where on est
        const oSpan = document.createElement("SPAN");
        oSpan.textContent = "[" + ref + "]";
        oSection.append(oSpan);
     
        // création des éléments
        if (DATAS[ref].titre) {
          const oTitre = document.createElement("H2");
          oTitre.textContent = DATAS[ref].titre;
          oSection.append(oTitre);
        }
     
        // la question
        const oText = document.createElement("P");
        oText.innerHTML = DATAS[ref].text || "Oups !!";
        oSection.append(oText);
     
        // création des boutons
        const oCdeBtn = document.createElement("P");
        oCdeBtn.classList.add("btn-next");
     
        const actions = DATAS[ref].action || [];
        actions.forEach((act) => {
          const oBtn = document.createElement("BUTTON");
          oBtn.textContent = act.label;
          // ajout événement click
          oBtn.addEventListener("click", (e) => {
            removeNextEtape(oBtn);
            oBtn.classList.add("actif-btn");
            createNextEtape(act.goto);
          });
          // facultatif !
          oBtn.disabled = !DATAS[act.goto];
          // ajout des éléments crées
          oCdeBtn.append(oBtn);
        });
        // ajout des éléments crées
        oSection.append(oCdeBtn);
        oDest.append(oSection);
        // applique animation
        setTimeout(() => oSection.classList.remove("animated"), 10);
      }
    }
    // lancement questionnaire
    createNextEtape("depart");
    </script>
    </body>
    </html>
    Je vous laisse décortiquer le code pour plus d'informations


    Le fichier de test en ligne :

  14. #14
    Candidat au Club
    Homme Profil pro
    Autres
    Inscrit en
    Mars 2020
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Autres

    Informations forums :
    Inscription : Mars 2020
    Messages : 10
    Points : 4
    Points
    4
    Par défaut
    Bonsoir,

    Pas mal cette approche, je vais la tester et je te dis.

    Vous m'impressionnez en tout cas vous avez un super niveau je me suis pas trompé en postant ici

    Merci beaucoup, d'ici vendredi je teste ta méthode NoSmoking

Discussions similaires

  1. [2.x] Refactoriser code controleur
    Par badjinmb dans le forum Symfony
    Réponses: 2
    Dernier message: 11/01/2013, 17h15
  2. Refactoriser du code existant
    Par SrK dans le forum Général Java
    Réponses: 6
    Dernier message: 24/04/2012, 14h48
  3. De la rapidité du code
    Par jfloviou dans le forum Contribuez
    Réponses: 233
    Dernier message: 29/05/2009, 02h17
  4. Explorateur de code C
    Par Zero dans le forum C
    Réponses: 14
    Dernier message: 06/06/2002, 09h41
  5. OmniORB : code sous Windows et Linux
    Par debug dans le forum CORBA
    Réponses: 2
    Dernier message: 30/04/2002, 17h45

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