Un product finder wizard qui pose une série de questions pour aider l’utilisateur à trouver le bon produit en fonction de ses besoins. La fonctionnalité est constituée de:
- le Drilldown de Foundation 6 pour la mise en place du wizard
<ul class="vertical menu drilldown product-finder-tree">
- des boutons de navigation Précédent, Réinitialiser et Suivant
<div class="product-finder-actions">
- un fil d’Ariane qui se constitue à la volée en fonction des choix précédents de l’utilisateur
<div id="productFinderBreadcrumb" class="product-finder-breadcrumb"></div>
Partie PHTML
Le code HTML qui constitue le menu n’est autre qu’une liste non-ordonnée d’éléments imbriqués. Il est conforme au markup générique fourni par Zurb pour la démo du composant Drilldown aux exceptions près que:
- les éléments
<a>
portent une classe.question
- les éléments
<li>
ont un IDquestionID_
(généré via PHP dans notre exemple)
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 |
<?php /** @var \Vital\OptiChoix\Block\Index $block */ $tree = $block->getTree(); function displayLeaf($question) { ?> <li class="optioignons-leaf" id="questionID_<?=$question["id"];?>" data-category-id="<?=$question["id"];?>"> <a class="question" href="#"> <?=$question["label"];?> </a> <ul> <li> <div id="optioignos-product-list-<?=$question["id"];?>"></div> </li> </ul> </li> <?php } function displayQuestion($question) { if (!$question['children']) { displayLeaf($question); return; } ?> <li id="questionID_<?=$question["id"];?>"> <a class="question" href="#"> <?=$question["label"];?> </a> <?php if(count($question["children"])): ?> <ul id="<?=$question["id"];?>"> <?php foreach ($question["children"] as $child): displayQuestion($child); endforeach; ?> </ul> <?php endif; ?> </li> <?php } ?> <h2>Contenu des catégories et sous catégories</h2> <div id="productFinderBreadcrumb" class="product-finder-breadcrumb"></div> <div class="product-finder-actions"> <button id="productFinderActionPrevious" class="primary" role="button" disabled>Précédent</button> <button id="productFinderActionReset" class="primary" role="button" disabled>Résumer</button> <button id="productFinderActionNext" class="primary" role="button" disabled>Suivant</button> </div> <nav id="productFinder" class="product-finder"> <ul class="vertical menu drilldown product-finder-tree"> <?php displayQuestion($tree); ?> </ul> </nav> |
Partie jQuery
Nous utilisons plusieurs méthodes natives du composant Drilldown de Foundation 6:
-
_hideAll pour réinitialiser le menu à son état initial.
Attention: cette méthode semble bugguée. Je l’ai peut-être aussi mal exploitée, mais j’ai dû écrire un fix; voir ci-dessous:
1234567$productFinder.foundation('_hideAll');// FIX d'un bug du composant Drilldown de Foundation:// à l'utilisation de la méthode _hideAll, les aria-expanded restent à true$('[aria-expanded="true"]', $productFinder).each(function(){$(this).attr('aria-expanded', false);}); -
_showMenu pour rediriger l’utilisateur vers un choix précédent dans le DOM du menu (lorsqu’il clique sur le bouton Précédent).
Attention: il est important de pointer un élément
UL
(dans mon exemple, sur la classe.submenu
) pour que cette méthode fonctionne!1$productFinder.foundation('_showMenu', $theTargetSubMenuUlTag); -
_show pour rediriger l’utilisateur plus en avant dans ses choix dans le DOM du menu (lorsqu’il clique sur le bouton Suivant).
Attention: il est important de pointer un élément
LI
(dans mon exemple, sur l’IDquestionID_
se trouvant sur lesLI
) pour que cette méthode fonctionne!1$productFinder.foundation('_show', $theCurrentElementWithASubmenuToOpen);
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 |
<script type="text/javascript"> require([ "jquery", "foundation", "domReady!" ], function ($, Foundation) { 'use strict'; $(".optichoix-leaf").on("click", function () { optiChoixLoadProducts(this.getAttribute("data-category-id")); }); function optiChoixLoadProducts (id) { $("#optichoix-product-list-"+id).load( "/expertise/productlist", "categoryId=" + id ); } // Product Finder // https://get.foundation/sites/docs/drilldown-menu.html let $productFinder = $("#productFinder"), $productFinderDrilldown = new Foundation.Drilldown($productFinder), $productFinderBreadcrumb = $('#productFinderBreadcrumb'), $productFinderActionPrevious = $('#productFinderActionPrevious'), $productFinderActionReset = $('#productFinderActionReset'), $productFinderActionNext = $('#productFinderActionNext'), navHistory = [], currentPositionInNavHistory = -1; // "-1" car avec "0" (valeur du 1er index dans un array), nous aurions déjà répondu à une 1ère question // On réinitialise la totale function resetAll(){ // On referme le menu $productFinder.foundation('_hideAll'); // FIX d'un bug du composant Drilldown de Foundation: // à l'utilisation de la méthode _hideAll, les aria-expanded restent à true $('[aria-expanded="true"]', $productFinder).each(function(){ $(this).attr('aria-expanded', false); }); // On désactive les boutons Previous, Reset et Next $productFinderActionPrevious.prop('disabled', true); $productFinderActionReset.prop('disabled', true); $productFinderActionNext.prop('disabled', true); // On réinitialise l'historique de navigation navHistory = []; // On réinitialise la position courante dans l'historique de navigation currentPositionInNavHistory = -1; // On met à jour le breadcrumb updateBreadcrumb(); } // On génère le breadcrumb function updateBreadcrumb(){ $productFinderBreadcrumb.empty(); $('[aria-expanded="true"]', $productFinder).each(function(){ $('<p class="product-finder-breadcrumb-element">'+$(this).attr('aria-label')+'</p>').appendTo($productFinderBreadcrumb); }); } // Au clic sur un choix proposé dans l'arbre des questions $('.product-finder-tree', $productFinder).on('click', '.question', function(){ let $this = $(this); let $closestExpanded = $this.closest('[aria-expanded="true"]'); // On pousse l'ID de la question cliquée dans le tableau navHistory navHistory.push({ questionID: $closestExpanded.attr('id') }); // On met à jour la position courante dans l'historique de navigation currentPositionInNavHistory = navHistory.length -1; // On met à jour le breadcrumb updateBreadcrumb(); // On active les boutons Previous et Reset $productFinderActionPrevious.prop('disabled', false); $productFinderActionReset.prop('disabled', false); }); // Au clic sur Reset button // ...on invoque la fonction resetAll $productFinderActionReset.on('click', resetAll); // Au clic sur Previous button $productFinderActionPrevious.on('click', function(){ // On récupère l'ID de la question correspondant à la currentPositionInNavHistory dans le tableau navHistory let currentPositionInNavHistoryQuestionID = $('#'+navHistory[currentPositionInNavHistory].questionID); // On met à jour le menu // (dans le DOM, on remonte d'un cran dans l'arbre des questions) let $theTargetSubMenuUlTag = currentPositionInNavHistoryQuestionID.closest('.submenu'); $productFinder.foundation('_showMenu', $theTargetSubMenuUlTag); // -1 sur la valeur de currentPositionInNavHistory currentPositionInNavHistory = currentPositionInNavHistory -1; // Si la valeur de la position précédente est "-1", on réinitialise tout if(currentPositionInNavHistory == "-1"){ resetAll(); // Sinon... } else { // On met à jour le breadcrumb updateBreadcrumb(); // On active le bouton Next $productFinderActionNext.prop('disabled', false); } }); // Au clic sur Next button $productFinderActionNext.on('click', function(){ // +1 sur la valeur de currentPositionInNavHistory currentPositionInNavHistory = currentPositionInNavHistory +1; // On met à jour le menu // (dans le DOM, on descend d'un cran dans l'arbre des questions) let $theCurrentElementWithASubmenuToOpen = $('#'+navHistory[currentPositionInNavHistory].questionID); $productFinder.foundation('_show', $theCurrentElementWithASubmenuToOpen); // On met à jour le breadcrumb updateBreadcrumb(); // Si la valeur de la position suivante correspond au dernier index du tableau navHistory // (l'utilisateur n'est jamais allé plus loin dans l'arbre des questions) let navHistoryLastIndex = navHistory.length -1; if(navHistoryLastIndex == currentPositionInNavHistory){ // On désactive le button Next $productFinderActionNext.prop('disabled', true); } else { // Nada... return false; } }); }) </script> |