Navigation✨ Premium

Popover Menu Chain

Panneau popover positionné dynamiquement sous son déclencheur — sous-menus enchaînés au survol avec détection de débordement et bascule automatique côté viewport.

JSPopoverMenu

Vous aurez accès à la démo interactive avec un compte gratuit.

Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Navigation comprend 36 effets, dont 4 gratuits. Découvrir la catégorie →

3 exemples d'usage

Barre de menus d'un éditeur en ligne — Fichier / Éditer / Affichage — Popover Menu Chain exemple 1

① Barre de menus d'un éditeur en ligne — Fichier / Éditer / Affichage

QuandDans un éditeur de code ou de texte riche embarqué dans une page web, quand l'utilisateur clique sur une entrée de la barre d'outils.
PourquoiLe positionnement par getBoundingClientRect garantit l'alignement précis sous chaque bouton même en layout fluide — le flip automatique évite tout débordement sur les viewports étroits.
Réglagesbackground: #1e1b4b (ton IDE sombre), transition opacity 0,15 s pour la réactivité, sous-menus imbriqués jusqu'à 2 niveaux.
Menu compte utilisateur — Dashboard SaaS avec préférences imbriquées — Popover Menu Chain exemple 2

② Menu compte utilisateur — Dashboard SaaS avec préférences imbriquées

QuandL'utilisateur clique sur son avatar dans le header d'une application SaaS — le panneau ouvre Profil, Paramètres (avec sous-menu Thème) et Déconnexion.
PourquoiLes sous-menus chainés regroupent des préférences de second niveau sans alourdir le menu principal — un seul sous-menu reste ouvert à la fois.
RéglagesDéclencheur = avatar 36 × 36 px, panneau min-width 180 px, bordure rgba(blanc, 0,15) sur fond #111827.
Menu contextuel d'un canvas de design — Mise en forme et export — Popover Menu Chain exemple 3

③ Menu contextuel d'un canvas de design — Mise en forme et export

QuandL'utilisateur clique sur le bouton d'actions d'un élément sélectionné dans un canvas de type Figma-like pour accéder à Aligner, Exporter (PNG / SVG / PDF) et Dupliquer.
PourquoiLa détection de débordement et le flip automatique sont essentiels dans un canvas à taille variable — le menu reste toujours dans le viewport sans gestion manuelle.
RéglagesPanneau min-width 170 px, gap 4 px entre parent et sous-menu (modifier le +6 dans le JS), sous-menus pour Aligner → Gauche/Centre/Droite et Exporter → PNG/SVG/PDF.

Comment ça marche

Au clic sur le bouton déclencheur, le script appelle getBoundingClientRect() sur le bouton et sur son ancêtre .popover-scene pour calculer les coordonnées relatives au conteneur. Il applique directement style.left et style.top sur le panneau — aucun position: sticky ni CSS Anchor Positioning : le calcul est rejoué à chaque ouverture, ce qui garantit l'alignement même si la page a défilé.

Les items portant data-has-sub déclenchent leur sous-panneau au mouseenter : le script ferme d'abord tous les .sub-popover.open déjà actifs (un seul sous-menu ouvert à la fois), puis ajoute la classe open au sous-menu cible via document.getElementById(id).

La détection de débordement s'exécute dans un requestAnimationFrame juste après l'ouverture : si getBoundingClientRect().right du sous-menu dépasse window.innerWidth − 10, la position bascule vers style.left = 'auto' et style.right = 'calc(100% + 6px)' — le sous-menu se replie à gauche du parent.

La fermeture est gérée par un seul écouteur click sur document : si la cible n'appartient ni au panneau ni au déclencheur (contains + comparaison directe), la fonction closeAll retire la classe open du panneau et de tous ses sous-menus. Les transitions CSS (opacity .2s, transform .2s) animent l'apparition et la disparition.

Accessibilité

  • prefers-reduced-motion : aucune vérification dans le code — les transitions opacity .2s, transform .2s restent actives. Complétez avec @media (prefers-reduced-motion: reduce) { .popover-panel, .sub-popover { transition: none } } si nécessaire.
  • Les items de menu sont des <button> natifs — focusables au clavier et annoncés par les lecteurs d'écran de base.
  • ARIA manquant : ni aria-expanded sur le déclencheur, ni aria-haspopup, ni role="menu" / role="menuitem" — à compléter pour une conformité WCAG complète.
  • Fermeture par Escape non implémentée — les utilisateurs clavier doivent cliquer à l'extérieur du panneau.
  • Contraste des items (#c7d2fe sur #1e1b4b) : ratio approximatif 6,5:1 — conforme WCAG AA pour le texte de taille normale.

Compatibilité navigateur

Utilise getBoundingClientRect, classList, requestAnimationFrame et querySelectorAll — supportés partout sans polyfill. Le pattern « Popover API » est simulé en JS pur, sans l'attribut natif popover.

Chrome 88+✓ Complet
Firefox 87+✓ Complet
Safari 14+✓ Complet
Edge 88+✓ Complet
Mobile iOS✓ Touch OK (survol désactivé)
Android Chrome✓ Complet

Sans JS, le panneau reste masqué (<code>opacity:0, pointer-events:none</code>) — aucune erreur fatale, le déclencheur reste visible et focusable.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
<div class="popover-scene" id="popoverScene">
  <button class="popover-trigger-btn" id="popoverTrigger">Menu</button>
  <div class="popover-panel" id="popoverPanel" style="position:absolute;">
    <button class="pop-item" data-has-sub="sub1">Rubrique <span class="arrow">▸</span></button>
    <button class="pop-item">Action directe</button>
    <div class="sub-popover" id="sub1">
      <button class="pop-item">Sous-item 1</button>
      <button class="pop-item">Sous-item 2</button>
    </div>
  </div>
</div>
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
data-has-sub="subId" absent Ajoutez ce data-attribute à un .pop-item pour qu'il ouvre le #subId correspondant au survol. Autant de sous-menus que nécessaire.
.popover-panel, .sub-popover { background } #1e1b4b Couleur de fond du panneau et des sous-menus. Passez à #111827 pour un fond plus neutre ou à #fff pour un thème clair.
.popover-panel, .sub-popover { border-color } rgba(99,102,241,.25) Bordure du panneau. Utilisez la couleur primaire de votre brand.
.pop-item:hover { background } rgba(99,102,241,.15) Couleur de survol des items. Adaptez à votre palette (ex. rgba(255,255,255,.08) pour un hover neutre).
transition: opacity .2s, transform .2s .2s Durée d'animation dans le CSS de .popover-panel et .sub-popover. Passez à .12s pour plus de réactivité ou 0s pour l'instant.
gap déclencheur → panneau (JS) 6 px Dans le JS : t.style.top = s.bottom - c.top + 6 + 'px' — le 6 est l'écart en pixels entre le bas du bouton et le haut du panneau.
gap panneau → sous-menu (CSS) calc(100% + 6px) Propriété left de .sub-popover — contrôle le gap horizontal entre le bord droit du panneau parent et le sous-menu.
.arrow (▸) Indicateur de sous-menu sur les items. Remplacez par une icône SVG ou un caractère Unicode (ex. ›, →, ▶).

FAQ

Non. Le nom « Popover API pattern » fait référence au comportement (panneau flottant positionné dynamiquement), pas à l'attribut HTML5 natif. L'effet est entièrement en JS vanilla avec getBoundingClientRect et classList — compatible Safari 14 et Firefox 87 sans polyfill.
Ajoutez <button class="pop-item" data-has-sub="monSousMenu">Mon item <span class="arrow">▸</span></button> dans #popoverPanel, puis <div class="sub-popover" id="monSousMenu">…</div> juste après. Le script détecte automatiquement tous les [data-has-sub] via querySelectorAll.
Le script ne dispose pas d'API publique. Pour ouvrir, déclenchez un click synthétique : document.getElementById('popoverTrigger').click(). Pour fermer, appelez document.getElementById('popoverPanel').classList.remove('open') puis retirez .open de tous les sous-menus avec querySelectorAll('.sub-popover.open').