[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"theme-db":3,"$fLE9-Ojm40FyTwN4akKrTZO-9U0JzZcbdgaLvSGzAILQ":22,"megamenu":61,"$fKnz2vuX4bZz1LbUTiuFsvSZ3e07l5_5fqNYp4Tzdhi8":130,"footer-db":198,"$f5HCn9Rj1MfrToBE8DtlcP4MbfP1te4GLpqdoUgqXHPA":216,"header-db":230},{"theme":4},{"colors":5,"typography":13,"ui":17,"defaultColorMode":21},{"primary":6,"secondary":7,"background":8,"foreground":9,"muted":10,"headerBg":11,"footerBg":12,"topBarBg":9,"topBarText":11},"#4F46E5","#0D9488","#F9FAFB","#111827","#6B7280","#ffffff","#020617",{"fontFamily":14,"fontUrl":15,"baseFontSize":16},"Inter, system-ui, sans-serif","https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Inter:wght@400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;0,800;0,900;1,400;1,700&display=swap","16px",{"borderRadius":18,"contentWidth":19,"shadow":20},"lg","7xl",true,"light",{"title":23,"slug":24,"metaDescription":25,"category":26,"tags":27,"difficulty":34,"psVersions":35,"content":39,"faq":40,"tldr":56,"readingTime":57,"generatedAt":58,"publishDate":58,"relatedArticles":59,"sourceCategory":60},"Intégrer Bootstrap dans le menu PrestaShop : refonte du blocktopmenu","integrer-bootstrap-menu-prestashop-blocktopmenu","Guide complet pour remplacer le menu natif PrestaShop par un menu Bootstrap responsive. Code, bonnes pratiques et migration vers PrestaShop 8.","developpement",[28,29,30,31,32,33],"blocktopmenu","bootstrap","menu responsive","navigation","module prestashop","frontend","intermediaire",[36,37,38],"1.6","1.7","8.x","\u003Ch2>Pourquoi le menu natif PrestaShop pose problème\u003C\u002Fh2>\n\u003Cp>Le module \u003Ccode>blocktopmenu\u003C\u002Fcode> (renommé \u003Ccode>ps_mainmenu\u003C\u002Fcode> depuis PrestaShop 1.7) est fonctionnel, mais il souffre de plusieurs limitations qui frustrent les intégrateurs :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>**Comportement mobile bancal** : sur PrestaShop 1.6, les sous-menus nécessitaient parfois un double clic pour se déplier, ce qui dégradait l'expérience utilisateur sur mobile.\u003C\u002Fli>\n\u003Cli>**CSS et JS propriétaires** : le menu utilise ses propres styles et scripts au lieu de s'appuyer sur un framework CSS standard, ce qui complique la personnalisation.\u003C\u002Fli>\n\u003Cli>**Intégration limitée** : impossible d'ajouter nativement des éléments comme le panier ou la barre de recherche dans la barre de navigation.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>La solution la plus propre consiste à refondre le rendu du menu en s'appuyant sur les classes Bootstrap, tout en conservant la logique back-office de gestion des catégories.\u003C\u002Fp>\n\u003Ch2>Stratégie de refonte : modifier le module ou créer un override\u003C\u002Fh2>\n\u003Ch3>Option 1 : Override du module (recommandé)\u003C\u002Fh3>\n\u003Cp>Pour PrestaShop 1.6 et 1.7, la méthode la plus maintenable consiste à créer un override du module plutôt que de modifier directement ses fichiers :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-\">\n\u002Foverride\u002Fmodules\u002Fblocktopmenu\u002Fblocktopmenu.php\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Cela permet de conserver les mises à jour du module sans perdre vos personnalisations.\u003C\u002Fp>\n\u003Ch3>Option 2 : Template custom dans le thème\u003C\u002Fh3>\n\u003Cp>Sur PrestaShop 1.7+, privilégiez la surcharge du template dans votre thème :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-\">\n\u002Fthemes\u002Fvotre-theme\u002Fmodules\u002Fps_mainmenu\u002Fps_mainmenu.tpl\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Sur PrestaShop 8.x, le module \u003Ccode>ps_mainmenu\u003C\u002Fcode> utilise Smarty et la même logique de surcharge s'applique.\u003C\u002Fp>\n\u003Ch2>Réécrire le rendu HTML avec les classes Bootstrap\u003C\u002Fh2>\n\u003Cp>L'objectif est de remplacer le markup propriétaire par une structure Bootstrap standard. Voici un exemple de méthode \u003Ccode>generateCategoriesMenu\u003C\u002Fcode> refactorisée pour produire un menu dropdown Bootstrap :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-php\">\nprivate function generateCategoriesMenu($categories, $is_children = 0)\n{\n    $html = '';\n\n    foreach ($categories as $key =&gt; $category) {\n        $cat = new Category($category['id_category'], $this-&gt;context-&gt;language-&gt;id);\n        $link = Tools::htmlentitiesUTF8($cat-&gt;getLink());\n        $hasChildren = isset($category['children']) && !empty($category['children']);\n        $isActive = ($this-&gt;page_name === 'category'\n            && (int) Tools::getValue('id_category') === (int) $category['id_category']);\n\n        if ($hasChildren && !$is_children) {\n            \u002F\u002F Catégorie parent avec sous-menu\n            $html .= '&lt;li class=\"nav-item dropdown' . ($isActive ? ' active' : '') . '\"&gt;';\n            $html .= '&lt;a class=\"nav-link dropdown-toggle\" href=\"' . $link . '\" ';\n            $html .= 'data-bs-toggle=\"dropdown\" role=\"button\" aria-expanded=\"false\"&gt;';\n            $html .= htmlspecialchars($category['name'], ENT_QUOTES, 'UTF-8');\n            $html .= '&lt;\u002Fa&gt;';\n            $html .= '&lt;ul class=\"dropdown-menu\"&gt;';\n            $html .= $this-&gt;generateCategoriesMenu($category['children'], 1);\n            $html .= '&lt;\u002Ful&gt;';\n            $html .= '&lt;\u002Fli&gt;';\n        } else {\n            \u002F\u002F Catégorie sans enfant ou sous-catégorie\n            $cssClass = $is_children ? 'dropdown-item' : 'nav-link';\n            $html .= '&lt;li' . ($is_children ? '' : ' class=\"nav-item\"') . '&gt;';\n            $html .= '&lt;a class=\"' . $cssClass . ($isActive ? ' active' : '') . '\" ';\n            $html .= 'href=\"' . $link . '\"&gt;';\n            $html .= htmlspecialchars($category['name'], ENT_QUOTES, 'UTF-8');\n            $html .= '&lt;\u002Fa&gt;';\n            $html .= '&lt;\u002Fli&gt;';\n        }\n    }\n\n    return $html;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Points clés du code\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>**`data-bs-toggle=\"dropdown\"`** : attribut Bootstrap 5 (utilisez `data-toggle` pour Bootstrap 3\u002F4 sur PrestaShop 1.6).\u003C\u002Fli>\n\u003Cli>**Échappement HTML** : `htmlspecialchars()` avec `ENT_QUOTES` pour prévenir les failles XSS sur les noms de catégories.\u003C\u002Fli>\n\u003Cli>**Détection de la page active** : on compare `id_category` pour appliquer la classe `.active` et améliorer l'UX.\u003C\u002Fli>\n\u003Cli>**Récursivité propre** : le paramètre `$is_children` contrôle le niveau d'imbrication et adapte les classes CSS.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Intégrer le panier et la recherche dans la navbar\u003C\u002Fh2>\n\u003Cp>Une fois le menu converti en navbar Bootstrap, vous pouvez y intégrer d'autres éléments. Voici la structure HTML recommandée :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-html\">\n&lt;nav class=\"navbar navbar-expand-lg navbar-light bg-white\"&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;a class=\"navbar-brand\" href=\"{$link-&gt;getPageLink('index')}\"&gt;\n      &lt;img src=\"{$logo_url}\" alt=\"{$shop_name}\" height=\"40\"&gt;\n    &lt;\u002Fa&gt;\n\n    &lt;button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\"\n            data-bs-target=\"#mainNavbar\" aria-controls=\"mainNavbar\"\n            aria-expanded=\"false\" aria-label=\"Menu\"&gt;\n      &lt;span class=\"navbar-toggler-icon\"&gt;&lt;\u002Fspan&gt;\n    &lt;\u002Fbutton&gt;\n\n    &lt;div class=\"collapse navbar-collapse\" id=\"mainNavbar\"&gt;\n      &lt;!-- Menu catégories --&gt;\n      &lt;ul class=\"navbar-nav me-auto\"&gt;\n        {$menu_html}\n      &lt;\u002Ful&gt;\n\n      &lt;!-- Barre de recherche --&gt;\n      &lt;form class=\"d-flex me-3\" action=\"{$link-&gt;getPageLink('search')}\" method=\"get\"&gt;\n        &lt;input class=\"form-control\" type=\"search\" name=\"s\" placeholder=\"Rechercher...\"&gt;\n      &lt;\u002Fform&gt;\n\n      &lt;!-- Panier --&gt;\n      &lt;div class=\"navbar-cart\"&gt;\n        {hook h='displayNav'}\n      &lt;\u002Fdiv&gt;\n    &lt;\u002Fdiv&gt;\n  &lt;\u002Fdiv&gt;\n&lt;\u002Fnav&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>Corriger le bug du double-clic mobile\u003C\u002Fh2>\n\u003Cp>Sur PrestaShop 1.6, le menu responsive natif souffrait d'un bug connu : les boutons \"+\" pour déplier les sous-catégories nécessitaient deux clics. Ce problème venait d'un conflit entre les événements \u003Ccode>click\u003C\u002Fcode> et \u003Ccode>touchstart\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>Avec Bootstrap, ce bug disparaît car le framework gère nativement les interactions tactiles. Si vous gardez le JS natif, voici le correctif :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-javascript\">\n\u002F\u002F Remplacer l'event click par un gestionnaire propre\n$('.menu-mobile .arrow').on('touchstart click', function(e) {\n    e.preventDefault();\n    e.stopPropagation();\n    $(this).closest('li').toggleClass('open');\n    $(this).closest('li').find('&gt; .dropdown-menu').slideToggle(200);\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>Migration vers PrestaShop 8.x\u003C\u002Fh2>\n\u003Cp>Sur PrestaShop 8.x, le module \u003Ccode>ps_mainmenu\u003C\u002Fcode> reste basé sur Smarty mais le thème par défaut utilise Bootstrap 5. Si vous migrez depuis une version 1.6 :\u003C\u002Fp>\n\u003Col>\n\u003Cli>**Remplacez `data-toggle` par `data-bs-toggle`** dans tous vos templates.\u003C\u002Fli>\n\u003Cli>**Passez de jQuery à du JavaScript natif** si possible — PrestaShop 8 réduit sa dépendance à jQuery.\u003C\u002Fli>\n\u003Cli>**Utilisez les surcharges de template** (`\u002Fthemes\u002Fvotre-theme\u002Fmodules\u002Fps_mainmenu\u002F`) plutôt que les overrides PHP.\u003C\u002Fli>\n\u003Cli>**Testez le rendu mobile** : le `navbar-toggler` Bootstrap 5 gère nativement le responsive sans JS supplémentaire.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Bonnes pratiques\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>**Ne modifiez jamais les fichiers core du module** : utilisez les overrides ou les surcharges de template.\u003C\u002Fli>\n\u003Cli>**Videz le cache** après chaque modification : Back-office → Paramètres avancés → Performances.\u003C\u002Fli>\n\u003Cli>**Testez sur mobile** : le menu est l'élément le plus critique de la navigation mobile. Utilisez les DevTools Chrome en mode responsive.\u003C\u002Fli>\n\u003Cli>**Accessibilité** : conservez les attributs `aria-expanded`, `aria-label` et `role` pour la conformité WCAG.\u003C\u002Fli>\n\u003Cli>**Performance** : si vous ajoutez le panier au menu, utilisez un chargement AJAX pour éviter d'invalider le cache de page complète.\u003C\u002Fli>\n\u003C\u002Ful>",[41,44,47,50,53],{"q":42,"a":43},"Comment personnaliser le menu PrestaShop sans modifier le module d'origine ?","La méthode recommandée dépend de votre version. Sur PrestaShop 1.6, créez un override PHP dans \u002Foverride\u002Fmodules\u002Fblocktopmenu\u002F. Sur PrestaShop 1.7 et 8.x, privilégiez la surcharge du template Smarty en copiant le fichier .tpl dans \u002Fthemes\u002Fvotre-theme\u002Fmodules\u002Fps_mainmenu\u002F. Cette approche préserve la compatibilité avec les mises à jour du module.",{"q":45,"a":46},"Pourquoi le menu mobile PrestaShop 1.6 nécessite un double-clic pour ouvrir les sous-catégories ?","Ce bug provient d'un conflit entre les événements touchstart et click dans le JavaScript natif du module blocktopmenu. La solution la plus propre est de migrer vers un menu Bootstrap qui gère nativement les interactions tactiles. Si vous ne pouvez pas migrer, ajoutez un gestionnaire d'événement combiné touchstart\u002Fclick avec e.preventDefault() et e.stopPropagation() sur les boutons d'expansion.",{"q":48,"a":49},"Comment ajouter le panier et la barre de recherche dans le menu principal PrestaShop ?","Intégrez ces éléments dans une structure navbar Bootstrap. Utilisez le hook displayNav pour injecter le bloc panier, et ajoutez un formulaire de recherche avec l'action pointant vers la page search de PrestaShop. Cette approche centralise la navigation et améliore l'UX, surtout sur mobile où l'espace est limité. Pensez à charger le contenu du panier en AJAX pour ne pas impacter le cache de page.",{"q":51,"a":52},"Le module blocktopmenu existe-t-il encore sur PrestaShop 8 ?","Non. Depuis PrestaShop 1.7, blocktopmenu a été remplacé par ps_mainmenu. La logique de gestion des catégories en back-office reste similaire, mais le rendu frontend a évolué. Sur PrestaShop 8.x, ps_mainmenu utilise Bootstrap 5 et Smarty. Les attributs data ont changé (data-bs-toggle au lieu de data-toggle) et la dépendance à jQuery est réduite.",{"q":54,"a":55},"Faut-il utiliser un module tiers ou personnaliser le menu natif PrestaShop ?","Pour des besoins standards (mega-menu, icônes, colonnes), un module tiers comme MegaMenu d'Addons peut suffire et vous fera gagner du temps. Pour une intégration poussée (panier dans la navbar, design system custom, comportement spécifique), la personnalisation du module natif via surcharge de template est préférable car vous gardez le contrôle total du markup et des performances.","Le module blocktopmenu (ps_mainmenu depuis PS 1.7) peut être refactorisé pour utiliser les classes Bootstrap natives, ce qui résout les bugs mobile, facilite la personnalisation et permet d'intégrer panier et recherche directement dans la navbar.",5,"2026-03-21T13:18:00.000Z",[],"PrestaShop pour les développeurs",{"items":62},[63,74,82,90,98,107,114,122],{"id":64,"type":65,"label":66,"href":68,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":71,"children":72,"psChildren":73},41,"link",{"fr":67},"Expertise","\u002Fexpertise",null,false,0,[],[],{"id":75,"type":65,"label":76,"href":78,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":79,"children":80,"psChildren":81},42,{"fr":77},"Blog","\u002Fblog",1,[],[],{"id":83,"type":65,"label":84,"href":86,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":87,"children":88,"psChildren":89},43,{"fr":85},"Modules PrestaShop","\u002Fmodules",2,[],[],{"id":91,"type":65,"label":92,"href":94,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":95,"children":96,"psChildren":97},44,{"fr":93},"Outils IA","\u002Foutils-ia",3,[],[],{"id":99,"type":65,"label":100,"href":102,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":103,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":104,"children":105,"psChildren":106},45,{"fr":101},"Offre Starter ✨","\u002Foffre-starter",{"highlight":20},4,[],[],{"id":108,"type":65,"label":109,"href":111,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":57,"children":112,"psChildren":113},46,{"fr":110},"Academy","\u002Facademy",[],[],{"id":115,"type":65,"label":116,"href":118,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":119,"children":120,"psChildren":121},47,{"fr":117},"À propos","\u002Fa-propos",6,[],[],{"id":123,"type":65,"label":124,"href":126,"icon":69,"description":69,"badge":69,"groupTitle":69,"style":69,"gridColumns":69,"cssClass":69,"psCategoryId":69,"showPsChildren":70,"position":127,"children":128,"psChildren":129},48,{"fr":125},"Contact","\u002Fcontact",7,[],[],{"columns":131},[132,144,174,190],{"title":133,"links":134},"Plateforme",[135,137,140,141],{"label":136,"href":102,"external":70},"Offre Starter (2 500 €)",{"label":138,"href":139,"external":70},"Devenir Ambassadeur","\u002Fambassadeur",{"label":85,"href":86,"external":70},{"label":142,"href":143,"external":20},"CodeMyShop.com","https:\u002F\u002Fcodemyshop.com",{"title":145,"links":146},"Le Synedre",[147,150,153,156,159,162,165,168,171],{"label":148,"href":149,"external":70},"L'histoire","\u002Fsynedre",{"label":151,"href":152,"external":70},"Constitution","\u002Fsynedre\u002Fconstitution",{"label":154,"href":155,"external":70},"L'équipe","\u002Fequipe",{"label":157,"href":158,"external":70},"Le réacteur en direct","\u002Freacteur",{"label":160,"href":161,"external":70},"Le Drill (entraînement)","\u002Fdrill",{"label":163,"href":164,"external":70},"Protocole de réunion","\u002Fsynedre\u002Freunion",{"label":166,"href":167,"external":70},"Les agents IA","\u002Fagents-ia",{"label":169,"href":170,"external":70},"La Conduite","\u002Fsynedre\u002Fconduite",{"label":172,"href":173,"external":70},"Charte plateforme","\u002Fsynedre\u002Fcharte",{"title":175,"links":176},"Ressources",[177,178,179,182,184,187],{"label":77,"href":78,"external":70},{"label":110,"href":111,"external":70},{"label":180,"href":181,"external":70},"Dictionnaire","\u002Fdictionnaire",{"label":183,"href":68,"external":70},"Expertise PrestaShop",{"label":185,"href":186,"external":70},"Flywheel","\u002Fflywheel",{"label":188,"href":189,"external":70},"Manifeste","\u002Fmanifeste",{"title":117,"links":191},[192,194,197],{"label":193,"href":118,"external":70},"Alexandre Carette",{"label":195,"href":196,"external":70},"Dossier de presse","\u002Fpresse",{"label":125,"href":126,"external":70},{"footer":199},{"theme":200,"description":69,"hours":69,"logo":201,"contact":204,"social":205,"bottomBar":215},"dark",{"src":202,"href":203,"alt":193},"\u002Flogo-ac.svg","\u002F",{"email":69,"phone":69,"address":69,"cta":69},[206,209,212],{"platform":207,"href":208,"label":207},"linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Falexandre-carette\u002F",{"platform":210,"href":211,"label":210},"malt","https:\u002F\u002Fwww.malt.fr\u002Fprofile\u002Falexandrecarette",{"platform":213,"href":214,"label":213},"github","https:\u002F\u002Fgithub.com\u002Fprest4cafe",{"copyright":69},{"academy":217,"blog":218,"expertise":229},[],[219,223,226],{"title":220,"url":221,"score":79,"type":222},"PrestaShop headless avec Nuxt 3 : pourquoi séparer back et front","\u002Fblog\u002Fprestashop\u002Farchitecture\u002Fprestashop-headless-nuxt-separation-front-back","blog",{"title":224,"url":225,"score":79,"type":222},"PrestaShop headless : Nuxt 3, pas Next.js — le choix souverain","\u002Fblog\u002Fprestashop\u002Farchitecture\u002Fprestashop-headless-nuxt-nextjs-souverainete",{"title":227,"url":228,"score":79,"type":222},"Sylius rachète PrestaShop : ce que ça change pour vous","\u002Fblog\u002Fprestashop\u002Farchitecture\u002Fsylius-rachat-prestashop-headless-souverainete",[],{"header":231},{"logo":232,"topBar":235,"contactEmail":238,"features":239,"navBar":69},{"src":202,"alt":233,"text":193,"href":203,"class":234},"Alexandre Carette — Architecte E-commerce Souverain","h-10 w-10",{"message":69,"showLanguages":70,"align":236,"languages":237},"left",[],"contact@alexandrecarette.fr",{"showSearch":70,"showWishlist":70,"showLogin":20,"showContact":70,"showCart":70,"stickyHeader":20,"headerLayout":240},"inline"]