Composant de menu déroulant positionné en position: fixed
par rapport au déclencheur. Le positionnement est calculé en JS via getBoundingClientRect()
et se recalcule automatiquement au scroll et au resize. Supporte 4 directions d'ouverture avec alignements,
et gère automatiquement les collisions avec les bords de la page.
Un seul dropdown peut être ouvert à la fois.
Chaque data-target doit correspondre exactement à l'id
du .dropdown-body associé. Accepte #id ou id.
⚠️ Le .dropdown-body doit être un enfant direct du
.dropdown pour que le bouton déclencheur soit
correctement retrouvé lors du calcul de position.
Les attributs suivants sont ajoutés automatiquement par Dropdown.init()
:
aria-controls (lie le bouton à son
dropdown via l'id)
aria-expanded (état ouvert / fermé, mis à
jour à chaque toggle)
L’attribut suivant doit être posé manuellement dans le HTML :
hidden (sur le .dropdown-body fermé au chargement)
| CLASSE | ÉLÉMENT | DESCRIPTION |
|---|---|---|
.dropdown |
<div> |
Conteneur principal. S'affiche en inline-flex et sert
de référence pour retrouver le bouton déclencheur lors du calcul de position.
|
.dropdown-body |
<div> |
Panneau déroulant en position: fixed. Positionné par
le JS via getBoundingClientRect(). Gère la bordure,
le fond, le z-index et la transition d'opacité.
|
.show |
<div> |
Ajoutée par le JS à l'ouverture. Applique opacity: 1.
|
.hide |
<div> |
Ajoutée par le JS à la fermeture et à l'initialisation. Applique opacity: 0. L'attribut hidden est reposé à la fin de la transition.
|
Définit de quel côté du déclencheur le panneau s'ouvre. Le JS lit ces classes dans Dropdown.position() pour calculer top et left. Ces
classes s'appliquent sur .dropdown-body.
| CLASSE | DESCRIPTION |
|---|---|
.drop-bottom |
Ouvre le panneau en dessous du déclencheur. Si l'espace en bas de la page est insuffisant, bascule automatiquement au-dessus. |
.drop-top |
Ouvre le panneau au-dessus du déclencheur. Si l'espace en haut de la page est insuffisant, bascule automatiquement en dessous. |
.drop-right / .drop-end
|
Ouvre le panneau à droite du déclencheur. Si pas de place à droite, essaie à gauche. Si ni gauche ni droite, bascule en bas (ou en haut si pas de place en bas). |
.drop-left / .drop-start
|
Ouvre le panneau à gauche du déclencheur. Si pas de place à gauche, essaie à droite. Si ni gauche ni droite, bascule en bas (ou en haut si pas de place en bas). |
L'espace disponible est calculé par rapport à la hauteur totale de la page via
document.documentElement.scrollHeight — pas le viewport.
Ainsi, si le bouton est en bas de la page et qu'il ne reste plus de place pour scroller,
le dropdown bascule automatiquement vers le haut. Pour la largeur, c'est
window.innerWidth qui est utilisé.
Se combine avec .drop-top et .drop-bottom. Définit l'alignement du panneau sur l'axe
horizontal.
| CLASSE | DESCRIPTION |
|---|---|
.drop-align-left / .drop-align-start
|
Aligne le bord gauche du panneau sur le bord gauche du déclencheur. |
.drop-align-center / .drop-align-middle |
Centre le panneau horizontalement sous/au-dessus du déclencheur. Défaut si aucun alignement n'est
spécifié pour .drop-top et .drop-bottom.
|
.drop-align-right / .drop-align-end |
Aligne le bord droit du panneau sur le bord droit du déclencheur. |
Se combine avec .drop-left et .drop-right. Définit l'alignement du panneau sur l'axe
vertical.
| CLASSE | DESCRIPTION |
|---|---|
.drop-align-top |
Aligne le bord haut du panneau sur le bord haut du déclencheur. |
.drop-align-middle / .drop-align-center |
Centre le panneau verticalement par rapport au déclencheur. Défaut si aucun alignement n'est
spécifié pour .drop-left et .drop-right.
|
.drop-align-bottom |
Aligne le bord bas du panneau sur le bord bas du déclencheur. |
Si aucune classe de direction n'est posée, le JS place le panneau en bas centré sous le déclencheur.
| ÉTAT | ÉLÉMENT | DESCRIPTION |
|---|---|---|
| Fermé | [hidden].hide |
État initial. L'attribut hidden masque le panneau. La
classe .hide applique opacity: 0.
|
| Ouvert | .show |
hidden retiré. La classe .show applique opacity:
1. aria-expanded="true" mis à jour. Les
listeners scroll et resize sont actifs.
|
| En fermeture | .hide |
.show retiré, .hide ajouté. Transition CSS en cours. hidden reposé à la fin de transitionend. Les listeners sont retirés.
|
Le composant est piloté par la classe Dropdown. Elle s'appuie
sur la délégation d'événements
(un seul listener sur document), le positionnement en position: fixed
via getBoundingClientRect(), et des listeners scroll /
resize avec {
passive: true } actifs uniquement quand un dropdown est ouvert.
import Dropdown from './Dropdown.js';
// A single instance is enough for the entire page.
new Dropdown();
La classe implémente un guard Dropdown.initialized :
instancier Dropdown plusieurs fois n'entraîne aucun
doublon de listeners.
| ATTRIBUT | ÉLÉMENT | DESCRIPTION |
|---|---|---|
data-ui="dropdown" |
.btn |
Identifie le bouton comme déclencheur du dropdown. |
data-target |
.btn |
Sélecteur du .dropdown-body cible. Accepte #id ou id.
|
| MÉTHODE | DESCRIPTION |
|---|---|
Dropdown.init() |
Parcourt tous les boutons [data-ui="dropdown"],
initialise aria-controls, aria-expanded et les classes .show / .hide. Si un dropdown est ouvert par défaut au
chargement, branche également les listeners scroll et
resize.
|
Dropdown.isOpen(dropdown) |
Retourne true si le dropdown n'a pas l'attribut hidden.
|
Dropdown.toggleDropdown(dropdown,
button) |
Bascule l'état du dropdown. Appelle openDropdown ou
closeDropdown selon Dropdown.isOpen().
|
Dropdown.openDropdown(dropdown,
button) |
Retire hidden, calcule la position via Dropdown.position(), échange .hide contre .show,
met à jour aria-expanded="true" et branche les
listeners scroll / resize.
|
Dropdown.closeDropdown(dropdown,
button) |
Échange .show contre .hide, met à jour aria-expanded="false", retire les listeners et repose
hidden à la fin de transitionend.
|
Dropdown.closeAllDropdowns(exception) |
Ferme tous les dropdowns ouverts sauf le bouton passé en paramètre. Appelé avant chaque ouverture et
à chaque clic en dehors d'un .dropdown-body.
|
Dropdown.position(dropdown,
button) |
Calcule et applique top et left selon les classes de direction et d'alignement.
Gère les fallbacks de collision via document.documentElement.scrollHeight.
Appelée à l'ouverture et à chaque événement scroll /
resize.
|
Dropdown.createReposition(dropdown,
button) |
Crée et retourne la fonction de repositionnement associée à un dropdown. Utilise requestAnimationFrame et un flag rafPending pour limiter les recalculs à 60 par seconde
maximum. Appelée dans openDropdown() et init() pour brancher les listeners scroll et resize.
|
// Open a dropdown programmatically
const dropdown = document.getElementById('my-dropdown');
const btn = document.querySelector('[data-target="#my-dropdown"]');
Dropdown.openDropdown(dropdown,
btn);
// Close a dropdown programmatically
Dropdown.closeDropdown(dropdown,
btn);
// Close all dropdowns
Dropdown.closeAllDropdowns();
| TOKEN | RÔLE |
|---|---|
--dropdown-body-background-color
|
Couleur de fond du panneau |
--dropdown-body-border-radius
|
Rayon des coins du panneau |
--dropdown-body-border-color |
Couleur de la bordure |
--dropdown-body-border-style |
Style de bordure (solid, dashed…)
|
--dropdown-body-border-width |
Épaisseur de la bordure |
/* Global variables */
:root {
--dropdown-body-border-radius: 8px;
}
/* Light theme */
:root[data-theme="light"] {
color-scheme:
light;
--dropdown-body-border-color: #e5e5e5;
--dropdown-body-background-color: #ffffff;
}
/* Dark theme */
:root[data-theme="dark"] {
color-scheme:
dark;
--dropdown-body-border-color: #404040;
--dropdown-body-background-color: #262626;
}
| PRATIQUE | DÉTAIL |
|---|---|
aria-expanded |
Initialisé par Dropdown.init() et mis à jour à chaque
toggle. Communique l'état ouvert/fermé aux lecteurs d'écran.
|
aria-controls |
Initialisé par Dropdown.init(). Lie le bouton à son
panneau via l'id du .dropdown-body.
|
hidden |
L'attribut HTML natif masque le panneau aux technologies d'assistance. Géré automatiquement par le JS. |
Élément <button> |
Utilisez toujours un <button> comme déclencheur
pour assurer la navigation clavier native (Tab, Entrée, Espace).
|
| Focus visible | Stylez :focus-visible sur le bouton déclencheur et
les éléments du menu pour les utilisateurs clavier.
|
Le JS ne gère pas nativement la fermeture à la touche Escape
ni le piège du focus dans le panneau. Pour une accessibilité complète, il est recommandé d'ajouter
un listener sur keydown pour fermer le dropdown sur
Escape.
Toujours ajouter hidden sur le .dropdown-body
au chargement si le dropdown doit être fermé par défaut. Sans cet attribut,
Dropdown.isOpen() considère le panneau comme ouvert.
Combiner une classe de direction (.drop-top, .drop-bottom,
.drop-left, .drop-right) avec une
classe d'alignement
pour un positionnement précis. Sans classe de direction, le panneau s'ouvre en bas centré par défaut.
Ne pas instancier Dropdown avant le chargement du DOM.
Utilisez DOMContentLoaded ou placez le script en bas de
page.
Tout clic en dehors d'un .dropdown-body ferme
automatiquement
tous les dropdowns ouverts. Les clics à l'intérieur du panneau sont ignorés.
Quand un dropdown est ouvert, des listeners scroll et
resize avec { passive:
true }
recalculent la position en temps réel. Ils sont automatiquement retirés à la fermeture.
Dropdown · WEBDEV-UI DOCS / V1