Créer un site one page “propre” avec WordPress

Une façon parmi tant d’autre…

Récemment j’ai eu à réaliser un site pour une petite société dans le BTP.
Le client ayant peu de contenu, le site se voulait très simple, et se prêtait donc parfaitement à un site “one page” sous WordPress.

Je n’expliquerai pas ici les diverses raisons qui m’ont fait choisir WP comme CMS, tout comme les étapes de création / validation des maquettes : on s’en moque, rentrons directement dans le vif du sujet.

Dans cet article, ma référence à “la page” définit la home du site, le one page.


Ma vision

Afin de laisser la main à mon client sur l’administration du contenu de son site, il m’était inconcevable de réaliser un one page en copiant collant du gros html dans l’éditeur d’une page WP, comme on peut le voir dans pas mal de tutos sur le net.

Les modifications auraient été impossibles pour mon client (totalement allergique au html) et trop fastidieuses pour moi (j’aurais passé mon temps à modifier en local pour après tout re-coller en lieu et place de l’existant)

La manière la plus élégante et ergonomique pour gérer cela était donc de créer une page par section du one page.

Nous pouvons sans problème avec WP récupérer chaque post lié à une entrée de menu pour constituer notre one page.

Mais se pose alors une question : Comment gérer les url des pages ?

En effet, si je fais autant de pages que de sections, cela veut dire que mes pages existent réellement en terme d’URL et qu’elles sont consultables de manière indépendante…

Seulement, je ne veux pas gérer l’affichage d’une page individuelle, pour une raison simple : Avec peu de contenu, les pages seules ne ressembleront pas à grand chose. Je ne veux pas passer du temps à styler ces pages : je préfère me concentrer sur ma page principale.

Donc quid de “comment gérer ces pages seules”

La gestion des liens

Je vois deux approches possibles, une “cracra” et une plus élégante (selon moi)

La Cracra

On peut intercepter les liens en JS pour les faire renvoyer à des parties spécifiques de “la page”. Pour éviter le référencement par les moteurs, nous pouvons ajouter l’attribut “rel=nofollow” sur les liens du menu.

C’est rapide et fonctionnel, mais ce qui me dérange dans cette approche, c’est que concrètement les pages seules sont toujours consultables, leurs liens sont présents dans le code source…

L’élégante

On modifie à la source le html injecté par WP au niveau du menu, et ajoute des redirections permanentes sur les pages seules.

Cela nécessite un peu plus de travail, mais on est sûr que les liens vers les pages seules ne sont jamais présents dans la page. On s’assure aussi que pour quelque raison que se soit, un internaute ou un robot sera toujours redirigé vers la page (et à la bonne section de surcroît pour un internaute).

Comment qu’on fait ?

Voilà donc les étapes mises en œuvres pour arriver à nos fins :

  1. Création des différentes pages/sections qui constitueront le one page.
  2. Création du menu principal qui listera et ordonnera l’ordre des sections.
  3. Construction du one page en récupérant les pages listées dans le menu.
  4. Customisation des liens du menu principal (transformation des url en hash).
  5. Mise en place de redirection individuelle sur chaque page.

Une dernière chose avant de détailler le process :
il existe une multitude de méthodes pour faire un one page sous WP : celle-ci en est une parmi tant d’autres et ne fait évidement pas foi comme “la solution”…

1. Création des différentes pages/ sections

Rien de sorcier ici. On créé des pages comme d’habitude sous WP.
Une remarque tout de même : dans cet exemple, je ne crée que des pages de 1er niveau. Je n’ai pas expérimenté la chose en créant des pages enfants.

Je suis sur une arborescence très simple constituée de 4 pages principales (Savoir-faire, Réalisations, l’Entreprise, Contact), une page “Home” (qui est notre one page) et une page secondaire (Mentions légales) .

Notre one page étant la page “Home”, on lui attribue un template spécial. C’est dans ce template que nous récupérerons toutes les autres pages.

L’ordre des pages n’a aucune influence sur l’ordre des sections de notre one page : cela sera géré par notre menu principal.

2. Création du menu principal

En adéquation avec mes pages précédemment créées, le menu principal “main-nav” reprend l’ensemble des pages principales (sauf la home bien sûr).

L’ordonnancement des entrées du menu définira l’ordre des sections sur le one page : on profite de toute la flexibilité de création de menu sous WP, pour ajouter, enlever, réordonner les différentes parties du one pagepas mieux…

3. Construction du one-page

Nous avons assigné un template particulier à notre page “Home” (template-home.php dans mon cas). Voyons maintenant le code nécessaire dans ce template pour récupérer les différentes pages constituant le one page en parcourant le menu principal.

Pour récupérer le contenu du menu sous forme d’un tableau, nous utilisons la fonction “wp_get_nav_menu_items” avec comme paramètre le nom de notre menu (ici “main-nav”) :

$menu_items = wp_get_nav_menu_items('main-nav');

Ensuite nous n’avons plus qu’à boucler sur le tableau obtenu afin de créer la requête nécessaire à la récupération de chaque page :

foreach ($menu_items as $menu_item ) {
$args = array(
'p' => $menu_item->object_id,
'post_type' => 'any'
);

Pour chaque itération, nous instancions une nouvelle requête “wp_query” afin de récupérer chaque page que nous injectons dans une nouvelle <section>.

Voilà le code complet :

<?php
$menu_items = wp_get_nav_menu_items('main-nav');
if( $menu_items ) {
foreach ($menu_items as $menu_item ) {
$args = array('p' => $menu_item->object_id,'post_type' => 'any');

global $wp_query;
$wp_query = new WP_Query($args);
$templatePart = ($menu_item->title == 'Réalisations') ? 'realisations' : $menu_item->object;
?>

<section <?php post_class('sep'); ?> id="<?php echo sanitize_title($menu_item->title); ?> ">
<?php
if ( have_posts() ){
include(locate_template('home-'.$templatePart.'.php'));
} ?>
</section>
<?php }}; ?>

Deux points importants dans ce code :

  • L’attribution d’un id (ou autre attribut) à chaque section .
  • La gestion de templates différents selon le type de pages récupérées via le menu.

Gestion des ID

Un point important dans un one page est la correspondance entre le menu et les sections, afin de proposer un scroll automatique à la position désirée dans la page.

Pour faire cela, on va donner un id à chaque section en reprenant le titre de l’entrée du menu correspondante, en mode “slug” (grâce à la fonction sanitize_title() ).

id="<?php echo sanitize_title($menu_item->title); ?>

Cela a également comme avantage de pouvoir bookmarker chaque section dans la page. Si on demande l’URL “www.monsite.com/#id”, le navigateur va automatiquement placer le scroll au bon niveau dans la page.

Si pour quelque raison que ce soit, le positionnement automatique du scroll via un id ne nous va pas (besoin d’un offset par exemple), nous pourrons toujours intercepter cela en JS, ou alors jouer avec un attribut “data-title” par exemple pour conserver la correspondance menu/section.


Attribution de templates

Avec 2 , 3 astuces, nous pouvons facilement assigner des templates différents pour chaque type de page.

L’idée est de créer dynamiquement une URL de template au regard des différents attributs récupérés sur les items du menu.

Dans mon exemple, je souhaite avoir un affichage particulier pour ma page “réalisation” afin d’afficher mes customs post_types “realisations”.
On créé donc 2 templates différents :

  • home-page.php pour toutes les pages.
  • home-realisations.php pour l’entrée réalisations.

Puis on assigne chaque template au contenu voulu en créant une variable pour constituer l’URL du template désiré :

  • La variable :
$templatePart = ($menu_item->title == 'Réalisations') ? 'realisations' : $menu_item->object;
  • URL ainsi créée :
include(locate_template('home-'.$templatePart.'.php'));

A titre d’exemple, voila le contenu de mes 2 templates ( à adapter bien évidemment):

  • home-pages.php
<?php while(have_posts()) : the_post(); ?>
<h2 class=”post-title”><?php the_title(); ?></h2>
<div class=”post-excerpt”><?php the_excerpt() ?></div>
<div class=”post-content”><?php the_content(); ?></div>
<?php endwhile; ?>
  • home-realisations.php
<?php while(have_posts()) : the_post(); ?>
<h2 class="post-title"><?php the_title(); ?></h2>
<div class="post-content"><?php the_content(); ?></div>
<?php
$args = array( 'posts_per_page'=>-1, 'post_typ'=>'realisation','orderby'=> 'menu_order', 'order'=> 'ASC');
$loop = new WP_Query( $args );
if ($loop->have_posts()) : while ($loop->have_posts()) : $loop->the_post();
$link = get_permalink($post->ID);
$thumbID = get_post_thumbnail_id($post->ID);
$postImg = wp_get_attachment_image_src($thumbID,'width=1140&crop=1' );
$baseline = $post->post_excerpt;
?>
<div class="realisation-card">
<a href="<?php echo $link; ?>" title="<?php echo $post->post_title; ?>" rel="prefetch">
<div class="wide-img" style="background-image:url(<?php echo $postImg[0]; ?>);"></div>
<div class="card-info">
<h3 class="card-title"><?php echo $post->post_title; ?></h3>
<?php if($baseline !='') { ?><h4 class="card-subtitle"><?php echo $baseline; ?></h4><?php } ?>
</div>
</a>
</div>
<?php endwhile;endif; ?>
<?php endwhile;

Pour finir, il nous faut préciser dans l’admin de WP l’utilisation de ce template comme page d’accueil de notre site.

4. Customisation des liens du menu principal

C’est le point charnière de cette méthode. C’est ici que nous allons supprimer toutes les références aux pages seules.

WordPress permet un custom profond de sa méthode wp_nav_menu via l’utilisation d’un “walker”.

Voila le “walker” en question qui vient se placer dans function.php :

class mono_walker extends Walker_Nav_Menu{
function start_el(&$output, $item, $depth, $args){
global $wp_query;
$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 ) .'"' : '';


$parsedURL = parse_url( esc_attr( $item->url ));
$cleanURL = substr_replace($parsedURL['path'],'',-1);//remove last '/';

$pathTab = explode('/',$cleanURL);
$pathTab[sizeof($pathTab)-1] = '#'.$pathTab[sizeof($pathTab)-1];
$path = implode('/',$pathTab );

$attributes .= ! empty( $item->url ) ? ' href="' . $path .'"' : '';
$attributes .= ! empty( $item->url ) ? ' data-title="' . sanitize_title($item->title) .'"' : '';
$description = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';

if($depth != 0) $description = "";

$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before .apply_filters( 'the_title', $item->title, $item->ID );
$item_output .= $description.$args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;

$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}

La partie qui nous intéresse se situe à partir de la variable $parsedURL.
C’est ici que l’on travaille pour transformer une URL de type “www.monsite.com/savoir-faire/” en “www.monsite.com/#savoir-faire” .

//Récupère l'URL DE l'item
$parsedURL = parse_url( esc_attr( $item->url ));
//Supprime le dernier '/' de l'url
$cleanURL = substr_replace($parsedURL['path'],'',-1);
//On split la chaine sur les '/'
$pathTab = explode('/',$cleanURL);
//On modifie la chaine derrière le dernier '/'
$pathTab[sizeof($pathTab)-1] = '#'.$pathTab[sizeof($pathTab)-1];
//On reconstitue l'URL complète modifiée
$path = implode('/',$pathTab );
//On injecte la nouvelle URL dans le href de l'item
$attributes .= !empty( $item->url ) ? ' href="'.$path.'"' : '';

L’explode / implode peut être dispensable, mais dans mon cas précis, le site est hébergé dans un sous-dossier (/v2). En travaillant ainsi les URLs, mon site peut être placé n’importe où (racine ou sous-dossier), le résultat sera correct.

J’en profite également pour remettre un petit coup de data-title dans mes items de menu afin d’avoir une correspondance parfaite de cet attribut entre menu et section. On pourra s’en servir pour gérer le scroll automatique en JS…ça mange pas de pain qu‘il se dit.

Ensuite nous n’avons plus qu’à appeler notre menu où nous voulons de manière classique, en précisant le “walker” :

<?php wp_nav_menu( array( 'theme_location' => 'main-menu','walker' => new mono_walker() ) ); ?>

Et voilà notre menu en sortie.

<div id="main-nav">
<nav class="container">
<ul id="menu-main-nav" class="menu">
<li id="menu-item-10" class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/#savoir-faire" data-title="savoir-faire">Savoir-Faire</a></li>
<li id="menu-item-46" class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/#realisations" data-title="realisations">Réalisations</a></li>
<li id="menu-item-28" class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/#lentreprise" data-title="lentreprise">L&rsquo;entreprise</a></li>
<li id="menu-item-34" class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/#contact" data-title="contact">Contact</a></li>
</ul>
</nav>
</div>

5. Mise en place de redirection 301

La nécessité de ce dernier point est discutable…
Normalement, à partir du moment où nous n’avons plus de références aux URL des pages seules, nous n’avons pas besoin de créer de redirection car elles ne seront jamais connues…

Ce point est donc plus là pour contrecarrer les petits malins qui peuvent essayer des URLs existantes, et se protéger aussi d’éventuels liens qui traîneraient…prudence est mère de sûreté…

Pour ce faire on peux utiliser le plugin Quick Page Post Redirect.
Celui-ci va nous permettre de gérer les redirections de chaque page de manière indépendante.

Ce plugin permet de s’assurer que chaque page seule demandée renvoie bien sur la page.

On peux ainsi dire que la page “savoir-faire” renvoie à “/#savoir-faire”.

C’est la partie un peu “tricky” de la chose, car selon la configuration de votre serveur, de où votre site est hébergé (racine ou sous-dossier), et des noms de pages, il faudra modifier les valeurs de redirection page par page…

Il est certainement possible d’automatiser cela avec une belle regexp dans un htaccess, mais je ne maîtrise absolument pas ce sujet.

Après avoir installé et activé le plugin, rendez-vous dans l’administration de chaque page.

De nouveaux champs sont apparus:

  • URL de redirection
  • Type de redirection

URL de redirection doit correspondre à l’id de votre section ou à tout autre élément qui vous permet de renvoyer à une section particulière (pour ma part j’utilise le data-title).

Pour la configuration du type de redirection, on se cale en 301 permanent : ainsi les crawlers retourneront sur la redirection et n’indexeront pas les pages seules.

L’utilisation de cette méthode est intéressante car cela permet de bookmarker facilement n’importe quelle section du one page.
Si j’envoie à un ami un lien du type “www.monsite.com/savoir-faire”, il sera automatiquement redirigé sur la home au bon niveau de scroll.


En compléments

Maintenant que l’on a une base “propre”, on peut venir jouer sur notre one page : ajout de scroll animé au clic sur le menu, changement du hash et du titre du document au fur et à mesure du scroll, changement de l’history, etc…fais ce qu’il te plaît..


En conclusion

C’est une méthode parmi beaucoup d’autres…

Peut-être un peu extrême en ce sens où l’on peut se limiter à l’expérience utilisateur, et dans ce cas ne faire qu’une simple interception des liens du menu en JS.

Pour ma part je trouve cette méthode bien plus “propre”, mais je suis extrêmement attentif à toutes autres méthodes, ou remarques sur celle que je viens de vous exposer.

En référence, vous pouvez visionner la vidéo de Dave Wallace, qui m’a fortement inspiré pour la gestion des templates dans la boucle qui récupère les différentes sections de “la page”.

En grand merci également à tous mes correcteurs !