Contenu principal
Slide Down Box Menu pour WordPress

Slide down box menu pour WordPress

Pour mon premier tutoriel, je vais vous expliquer comment réaliser le même menu que j’utilise sur screenfeed.fr, compatible avec WordPress 3.
Au programme: XHTML strict, php, css2 et 3, jQuery + easing, et bien sûr, des fonctions de WordPress.
Notre menu sera compatible à partir de WordPress 3, disposera d’une description pour chaque lien de premier niveau et d’un niveau de sous-menus (sans description cette fois), et sera bien sûr administrable.

Le menu original est tiré de cet article sur le site de Codrops, sauf qu’il n’est du tout prévu pour WordPress. C’est donc ce que nous allons faire aujourd’hui, il nous faudra modifier le code original et rajouter quelques lignes dans le fichier functions.php de notre thème. Je ne vais pas reprendre l’explication de la construction du menu puisque tout est déjà expliqué dans le tutoriel original (si vous n’êtes pas anglophobe ça devrait glisser tout seul ;) ). On va plutôt se concentrer sur les modifications et nouveautés à apporter.

Au début, il y avait…

Tout d’abord, si ce n’est déjà fait, il faut déterminer un emplacement pour notre menu dans notre thème, et on va appeler cet emplacement « Menu du haut » (c’est ce qui s’affichera dans la partie « Apparence » -> « Menus » de l’administration). Pour cela, on va éditer le fichier functions.php situé dans le dossier du thème:

01020304050607080910

add_action( 'init', 'slide_down_menus' );
if ( ! function_exists( 'slide_down_menus' ) ):
    function slide_down_menus() {
        register_nav_menus(
            array(
                'top-menu' => __( 'Menu du haut' )
            )
        );
    }
endif;

A partir de là, on peut créer un menu classique en ajoutant des liens vers nos pages, catégories, etc. depuis l’administration. Maintenant il nous faut placer notre menu sur le site, et préciser que l’on veut faire apparaitre la description des liens.
Pour ma part, j’ai placé mon menu dans le fichier header.php de mon thème:

010203040506070809101112

wp_nav_menu( array(
    'container' =>false,
    'menu_class' => 'nav',
    'echo' => true,
    'before' => '',
    'after' => '',
    'link_before' => '',
    'link_after' => '',
    'depth' => 2,
    'walker' => new description_walker(),
    'theme_location' => 'top-menu')
);

Concernant les arguments: j’ai mis 'container' => false car je ne souhaite pas englober ma balise ul dans une div ou autre (inutile dans mon thème, mais à vous d’adapter selon vos besoins), ‘depth’ correspond au nombre de sous-catégories dont vous aurez besoin, ‘walker’ est l’argument le plus important dans notre cas, c’est là qu’on indique une classe php qui nous est utile pour la description des liens et l’architecture du menu, et pour ‘theme_location’ on retrouve le ‘top-menu’ qu’on a indiqué précédemment dans le fichier functions.php.

Bien, maintenant on peut passer aux choses sérieuses: ajouter la description sous les liens de premier niveau (c’était ça l’idée à la base).
En même temps, il faudrait en profiter pour créer l’architecture html du menu.

Jetons déjà un coup d’œil à la structure du menu de Codrops.

010203040506070809101112131415161718

<ul id="sdt_menu" class="sdt_menu">
	<li>
		<a href="#">
			<img src="images/1.jpg" alt=""/>
			<span class="sdt_active"></span>
			<span class="sdt_wrap">
				<span class="sdt_link">Portfolio</span>
				<span class="sdt_descr">My work</span>
			</span>
		</a>
		<div class="sdt_box">
			<a href="#">Websites</a>
			<a href="#">Illustrations</a>
			<a href="#">Photography</a>
		</div>
	</li>
	...
</ul>

Dans le fond, ce n’est pas très différent d’un menu WordPress classique, on doit « juste » rajouter quelques balises.
Au final, on va arriver à ça:

0102030405060708091011121314151617

<ul id="menu-mainmenu">
    ...
    <li id="..." class="...">
        <a href="/ma-categorie/">
            <img src="/wp-content/themes/mon-theme/images/tab_category13.jpg" alt="" />
            <span class="tab_active"> </span>
            <span class="tab_wrap">
                <strong>Nom de ma catégorie</strong>
                <span>Description de ma catégorie</span>
            </span>
        </a>
        <ul class="sub-menu">
            <li id="..." class="..."><a href="/ma-categorie/ma-sous-cat-1/">Sous-catégorie 1</a></li>
            <li id="..." class="..."><a href="/ma-categorie/ma-sous-cat-2/">Sous-catégorie 2</a></li>
        </ul>
    </li>
</ul>

Quelques aménagements ont été faits afin d’être valide au niveau XHTML, se faciliter la vie pour le css, et pour ne pas se la compliquer avec WordPress (puisqu’il génère les balises ul et li automatiquement.

On ouvre le capot

Bien, pour construire cette structure, on va partir d’une fonction existante dans une classe php de WordPress. Pour les curieux, il s’agit de la fonction start_el de la classe Walker_Nav_Menu située dans le fichier /wp-includes/nav-menu-template.php, mais rassurez-vous, on ne va pas y toucher, on va la « réécrire » et la modifier dans notre fichier functions.php :

0102030405060708091011121314151617181920212223242526272829303132333435

class description_walker extends Walker_Nav_Menu {
    function start_el(&$output, $item, $depth, $args) {
        $indent = ( $depth ) ? str_repeat( "t", $depth ) : '';
        $class_names = $value = '';
        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
        $class_names = ' class="'. esc_attr( $class_names ) . '"';
        $output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';
        $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
        $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
        $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
        $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
        $prepend = '<strong>';
        $append = '</strong>';
        $description  = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';        //Si la description du lien n'est pas vide on l'enregistre
        if($depth != 0) {        //Si ce n'est pas un lien de premier niveau, on écrase tout
            $description = $append = $prepend = "";
        }
        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';        //On ouvre notre lien avec son url et ses classes
	if($item->menu_item_parent == 0) {        //S'il s'agit d'un lien de premier niveau
	    $item_output .= '<img src="'.get_bloginfo('template_directory').'/images/tab_'.$item->object.$item->object_id.'.jpg" alt="" />';        //L'image du lien
	    $item_output .= '<span class="tab_active"> </span>';        //Le span nécessaire à l'animation
	    $item_output .= '<span class="tab_wrap">';
	    $item_output .= $args->link_before .$prepend.apply_filters( 'the_title', $item->title, $item->ID ).$append;        //Le nom du lien (titre de la page)
	    $item_output .= $description.$args->link_after;        //La description
	    $item_output .= '</span></a>';        //On referme le lien
	} else {        //Si ce n'est pas un lien de premier niveau
	    $item_output .= $args->link_before .$prepend.apply_filters( 'the_title', $item->title, $item->ID ).$append;        //Le nom du lien
	    $item_output .= $args->link_after.'</a>';        //On referme le lien
	}
        $item_output .= $args->after;
        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
}

Le début de la fonction est identique à l’originale, puis on indique qu’on va utiliser des balises strong pour entourer le nom des liens de premier niveau. Ensuite, si la description du lien est disponible on l’entoure de nos span et on la met dans une variable.
Si on est en train de générer un lien dans un sous-menu, on n’a pas besoin de la description ni de nos balises strong donc on met une chaine vide dans nos variables précédentes.

On commence maintenant à générer notre lien en ouvrant la balise a, puis avec une condition « if » on va créer le reste, suivant s’il s’agit d’un lien de premier niveau ou d’un lien de sous-menu: un item de premier niveau n’a pas d’item parent, donc la valeur de $item->menu_item_parent sera 0.

L’image, c’est là que ça devient intéressant :) On va utiliser une petite astuce, mais il faudra remplir 3 conditions:
– il faudra placer chaque image dans… le dossier « images » de votre thème ( /wp-content/themes/mon-theme/images/ ) donc vérifiez que votre dossier s’appelle bien « images », ou modifiez le script,
– chaque image devra s’appeler « tab_xxxxxx123.jpg »: vous pouvez modifier le « tab_ » s’il ne vous plait pas bien sûr, quant au « xxxxxx123 » cela va dépendre du type de page que vous voulez afficher avec votre lien et de l’ID de votre page. Par exemple si vous voulez afficher les posts de la catégorie dont l’ID est 145, votre image devra s’appeler « tab_category145.jpg ». Pour une page dont l’ID est 72, « tab_page72.jpg »…
Comment trouver cet ID et ce « xxxxxx » me direz-vous? C’est très simple: dans l’administration, rentrez par exemple dans la page ou le post que vous voulez afficher, comme si vous vouliez la (le) modifier et regardez la fin de l’URL dans la barre d’adresse de votre navigateur: « …post.php?post=11&action=edit… » là il s’agit d’un POST et son ID est 11: tab_post11.jpg.
– toutes les images doivent avoir la même extension: .jpg mais vous pouvez aussi le changer si vous avez des images en .png, il suffit juste qu’elles soient toutes au même format.
La suite du code est facile, on construit les span dont on a besoin avec le nom et description du lien puis on fini notre condition « if » pour ensuite créer nos lien de sous-menus avec le « else ».

On s’habille pour l’hiver

Le plus gros est fait, il faudrait mettre tout ça en forme avec du css maintenant. Là, vous allez certainement devoir faire des modifications pour adapter le menu à votre thème. Je vous livre quand même un morceau de celui employé ici. Dans mon thème, j’avais besoin d’un menu affichant seulement 4 liens, de largeur fixe 480px, de hauteur 50px, d’une bordure de 1px, et des images de 120px de côté. Je vous fait grâce des font-family et images de fond. Le tout est à inclure dans le fichier css de votre thème:

001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109

a {
    cursor: pointer;
}
#menu-mainmenu {
    float: left;
    position: relative;
    top: 80px;
    width: 480px;
    height: 50px;
    border-style: solid;
    border-color: #ababab;
    border-width: 1px 1px 0 1px;
    list-style:none;
    background-color: #191919;
}
#menu-mainmenu li {
    float: left;
    width: 120px;
    height: 50px;
    position: relative;
    list-style-type: none;
}
#menu-mainmenu a {
    display: block;
    width: 120px;
    height: 50px;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 12;
    text-align: center;
    color: #ababab;
}
#header a:hover, #header a:active {
    text-decoration: none;
}
#menu-mainmenu a img {
    width: 0;
    height: 0;
    position: absolute;
    left: 120px;
    bottom: -1px;
    z-index: 100;
    -moz-box-shadow: 0 0 4px #000;
    -webkit-box-shadow: 0 0 4px #000;
    -khtml-box-shadow: 0 0 4px #000;
    box-shadow: 0 0 4px #000;
}
#menu-mainmenu .current-menu-item a img,
#menu-mainmenu .current_page_parent a img,
#menu-mainmenu .current-menu-ancestor a img {		/* On change la couleur et la taille de l'ombre des images pour l'onglet courant */
    -moz-box-shadow: 0 0 6px #00f5f1;
    -webkit-box-shadow: 0 0 6px #00f5f1;
    -khtml-box-shadow: 0 0 6px #00f5f1;
    box-shadow: 0 0 6px #00f5f1;
}
#menu-mainmenu .tab_active {				/* L'onglet qui va descendre sous l'image */
    width: 120px;
    height: 0;
    line-height: 0;
    position: absolute;
    left: -1px;
    top: 50px;
    z-index: 14;
    border-style: solid;
    border-width: 0 1px 1px 1px;
    border-color: #ababab;
    background-color: #110205;				/* Couleur de fond des onglets */
}
#menu-mainmenu .tab_wrap {				/* La span incluant nos liens et descriptions */
    width: 120px;
    height: 50px;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 15;
}
#menu-mainmenu a strong {
    display: block;
    height: 24px;
    padding-top: 7px;
    font-size: 24px;
    line-height: 24px;
}
#menu-mainmenu a span span {				/* Description */
    display: block;
    font-size: 12px;
    line-height: 18px;
    font-weight: normal;
}
#menu-mainmenu .sub-menu {
    display: none;
    width: 120px;
    height: 119px;
    position: absolute;
    left: 0;
    top: 50px;
    border: solid 1px #ababab;
    background-color: #110205;				/* Couleur de fond des sous-menus */
}
#menu-mainmenu .sub-menu li {
    height: 59px;					/* Hauteur prévue pour seulement 2 liens dans le sous-menu, à modifier selon vos besoins */
}
#menu-mainmenu .sub-menu a {
    height: 59px;					/* Hauteur prévue pour seulement 2 liens dans le sous-menu, à modifier selon vos besoins */
    line-height: 59px;					/* Hauteur prévue pour seulement 2 liens dans le sous-menu, à modifier selon vos besoins */
    font-weight: normal;
    font-size: 12px;
}

Un peu de mouvement

On passe enfin au javascript. Dans le dossier de votre thème, créez un dossier « js » où vous allez mettre vos scripts javascript, puis un fichier « main.js » puis collez-y ceci:

0102030405060708091011121314151617181920212223242526272829303132333435363738394041424344454647

jQuery(document).ready(function($){
    //-------------------------------------------------------------------------- Menu principal --------------------
    $(function() {
	$('#menu-mainmenu > li').bind('mouseenter',function(){
	    var $elem = $(this);
	    $elem.find('img')
		 .stop(true)
		 .animate({
		    'width':'120px',
		    'height':'120px',
		    'left':'0px'
		 },400,'easeOutBack')
		 .andSelf()
		 .find('.tab_wrap')
		 .stop(true)
		 .animate({'top':'85px'},500,'easeOutBack')
		 .andSelf()
		 .find('.tab_active')
		 .stop(true)
		 .animate({'height':'120px'},300,function(){
		    var $sub_menu = $elem.find('.sub-menu');
		    if($sub_menu.length){
			var left = '120px';
			if($elem.parent().children().length == $elem.index()+1) left = '-120px';
			$sub_menu.show().animate({'left':left},200,function(){$(this)});
		    }
		 });
	}).bind('mouseleave',function(){
	    var $elem = $(this);
	    var $sub_menu = $elem.find('.sub-menu');
	    if($sub_menu.length) $sub_menu.hide().css('left','0px');
	    $elem.find('.tab_active')
		 .stop(true)
		 .animate({'height':'0px'},300)
		 .andSelf().find('img')
		 .stop(true)
		 .animate({
		    'width':'0px',
		    'height':'0px',
		    'left':'50px'},400)
		 .andSelf()
		 .find('.tab_wrap')
		 .stop(true)
		 .animate({'top':'0'},300);
	});
    });
});

Il ne reste lus qu’à inclure les fichiers javascript dans votre fichier footer.php, juste après wp_footer(); (bien sûr il faudra que jQuery soit présent):

12

<script type='text/javascript' src="<?php bloginfo('stylesheet_directory'); ?>/js/jquery.easing.1.3.js"></script>
<script type='text/javascript' src="<?php bloginfo('stylesheet_directory'); ?>/js/main.js"></script>

Vous trouverez jquery.easing.1.3 ici.
Pour ma part, j’ai directement inclus jquery.easing.1.3.js dans main.js afin d’avoir une requête http en moins, à vous de voir ;)

Enjoy

Notre menu est terminé! Vous n’avez plus qu’à ajouter les images dans votre dossier avec les noms indiqués et le tour est joué.