Quantcast
Channel: Marien Fressinaud
Viewing all articles
Browse latest Browse all 172

Réaliser un "dropdown menu" en CSS

$
0
0

En développant FreshRSS, il a fallu que je fasse le choix d'utiliser un framework CSS ou non. J'ai donc commencé par lorgner sur Bootstrap et quelques autres. Bootstrap, on aime ou on n'aime pas, mais il faut bien avouer que c'est quand même bien fichu. Néanmoins une chose me gênait : JavaScript est requis pour l'utilisation des dropdowns. Puisqu'à l'époque je refusais d'obliger l'utilisateur à utiliser le JavaScript dans FreshRSS (ça a changé depuis…), je me suis dit que ça pourrait être intéressant de développer un système de dropdown entièrement en CSS. C'est ce système que je vous propose de découvrir aujourd'hui.

Une première couche de HTML

Un dropdown, ce n'est ni plus ni moins qu'un bouton ou un lien qui va ouvrir un menu lorsqu'on clique dessus. Du coup faisons la liste de ce dont nous avons besoin :

  • Un bouton (j'utiliserai ici une balise <a /> en fait)
  • Un bloc correspondant au menu (un simple <ul /> ici)
  • Des éléments faisant parti du menu (vous l'aurez compris, des balises <li />)
  • Tout ça sera regroupé dans un bloc parent correspondant au dropdown dans son ensemble (une balise <div />)

Pour le moment c'est assez simple, c'est du bête HTML que l'on assemble pour décrire la structure de notre dropdown. Et ça donne :

(Voir la démo)

Avec un peu de sucre

Le code précédant est très simple (quoique incomplet) mais ne fait absolument rien à part afficher un lien et une liste. Justement, cette liste on va l'arranger de façon à ce qu'elle ressemble à un menu. Un menu doit :

  • survoler la page
  • avoir un fond pour cacher ce qu'il survole
  • les éléments doivent se distinguer un minimum
.dropdown {
	/*
	La position relative est importante pour que le menu
	ne parte pas n'importe où. Le inline-block c'est parce qu'il s'agit
	généralement d'un bloc (on va jouer sur ses dimensions) mais que l'on va
	vouloir pouvoir disposer comme un élément de type inline
	*/
	position: relative;
	display: inline-block;
	text-align: left;
}
.dropdown-menu {
	/*
	La position absolute permet au menu de survoler la page.
	La taille minimale c'est arbitraire.
	Les margin et padding 0 c'est pour annuler les effets par défaut du <ul>
	(et oui, n'oubliez pas qu'il s'agit d'une liste avant tout !)
	*/
	position: absolute;
	min-width: 220px;
	margin: 0;
	padding: 0;
	background: #fff;
	border: 1px solid #ddd;
}
.dropdown-menu .item {
	/*
	Et on gère nos items comme on veut (le rendu final sera plus ou moins
	équivalent au rendu de Bootstrap)
	*/
	display: block;
	min-height: 30px;
	padding: 0 10px;
	line-height: 30px;
	font-size: 90%;
}
.dropdown-menu .item:hover {
	background: #0062be;
	color: #fff;
}

(Voir la démo)

Et on cache le tout !

J'espère que vous avez bien profité du menu que nous venons d'écrire car maintenant on va le cacher en rajoutant un display: none; au .dropdown-menu. Et oui, on ne veut pas l'afficher par défaut !

Maintenant on attaque le cœur du problème et la partie intéressante de ce petit tuto. Comment à partir d'un clic sur le bouton on va être en mesure d'afficher de nouveau ce menu ? Pour cela on va se baser sur la pseudo-classe :target : celle-ci permet de cibler un élément dont l'id est pointé par l'ancre présente dans l'url. C'est un poil compliqué pour simplement dire que si votre url ressemble à http://example.com/#mon-id alors l'élément <div id="mon-id" /> sera pointé par #mon-id:target ou même div:target. Vous pouvez bien entendu lire la documentation de Mozilla pour en savoir plus (attention, y a du spoil dans la doc !).

Avec cela en tête, on peut compléter le code HTML :

Avec cela on a de quoi compléter notre code CSS pour afficher le menu lorsque #dropdown-fruite est présent dans l'url :

.dropdown-menu:target {
	/*
	C'est ici que c'est important. Le :target indique que l'id du menu
	est présent dans l'url. On affiche donc le menu.
	*/
	display: block;
}

(Voir la démo)

Et oui, c'est tout ! :) On a un menu qui s'affiche lorsqu'on clic sur le lien et qui se ferme en cliquant sur la croix. Un dropdown c'est basiquement ça.

Et la cerise…

On a bien bossé mais c'est encore un peu moche. La croix de fermeture déjà n'est pas top et placée un peu n'importe où. Je vous propose de faire en sorte qu'en cliquant n'importe où sur la page, le menu se ferme. Pour cela on va étendre le lien de fermeture à toute la page. Il faut bien entendu que le lien soit placé au second plan, derrière le menu, afin de pouvoir continuer à manipuler les éléments du menu.

.dropdown-close a {
	/*
	On ne veut plus voir la croix donc font-size: 0 (ça pourrait être encore
	mieux fait, mais ça nous suffit ici)
	Le position: fixed permet que le lien de fermeture suive le scroll de la
	page et ainsi évite des comportements bizarres
	top/bottom/...: 0 fait que le lien s'étend aux quatre coins de l'écran !
	Et un z-index qui met le lien en second plan
	*/
	font-size: 0;
	position: fixed;
	top: 0; bottom: 0;
	left: 0; right: 0;
	display: block;
	z-index: -10;
}

Vous pouvez maintenant tester : le menu du dropdown se ferme en cliquant n'importe où sur la page sauf sur le menu lui-même.

Et en bonus je vous donne le code pour ajouter un petit triangle en haut du menu pour indiquer qu'il a été ouvert par le lien au-dessus (je ne sais pas s'il y a un nom à ce petit truc). Ça utilise le pseudo-élément :after :

.dropdown-menu:after {
	/*
	En gros on créé un élément carré de 10px/10px que l'on tourne de 45° et
	que l'on va placer juste au bon endroit pour faire croire qu'il s'agit
	d'un triangle qui dépasse du menu
	*/
	content: "";
	position: absolute;
	top: -6px;
	right: 13px;
	width: 10px;
	height: 10px;
	background: #fff;
	border-top: 1px solid #ddd;
	border-left: 1px solid #ddd;
	z-index: -10;
	transform: rotate(45deg);
}

(Voir la démo et même un peu plus)

Toujours plus haut, toujours plus fort !

La technique du :target marche très bien, ça fait des années que je joue avec. Ma première expérimentation avait consisté à créer une lightbox pour agrandir et mettre en avant des images en cliquant sur leur miniature. Avec ce que je viens de vous expliquer, ça ne devrait pas être trop compliqué : ajoutez un fond noir au lien de fermeture et remplacez les éléments du menu par une image et voilà :)

Il faut tout de même faire attention avec cette technique car elle "casse" l'historique de navigation de l'internaute ce qui peut être désagréable.

Notez que l'implémentation dans FreshRSS est un peu différente (elle utilise une balise de plus) parce qu'au moment de la mettre en place je n'avais pas encore trop bien saisi comment ça marchait exactement. Il faut aussi noter que le menu dropdown peut contenir tout un tas de choses dans FreshRSS comme des liens, des séparateurs ou des formulaires. Il faut donc faire en sorte que tout puisse s'afficher correctement.

J'espère que cet article vous sera utile, ça faisait (très) longtemps que je n'avais pas écrit de vrai tutoriel de ce genre, je vais peut-être m'y remettre parce qu'il y a tout plein de petits trucs rigolos comme ça en CSS avec lesquels on peut jouer.


Viewing all articles
Browse latest Browse all 172

Trending Articles