Notez que cet article a été écrit il y a plus de 3 ans, mais il n'est pas forcément obsolète. Il a été mis à jour il y a moins de 2 mois.
L’utilisation d’onglets dans une interface utilisateur peut aider le développeur à organiser le contenu de façon plus claire en distinguant différentes fonctionnalités d’un plugin ou d’un thème WordPress. Il est très fréquent de devoir gérer de nombreuses options sur la page des réglages.
Il faut pour cela définir :
- des liens ayant la classe nav-tab (les onglets),
- des div ayant la classe tabs (à afficher).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php function theplugin_options_page() { ?> <div class="wrap"> <h1><?php _e( 'The plugin', 'textdomain' ); ?></h1> <h2 class="nav-tab-wrapper"> <a href="#first-options" class="nav-tab"><?php _e( 'First Options', 'textdomain' ); ?></a> <a href="#second-options" class="nav-tab"><?php _e( 'Second Options', 'textdomain' ); ?></a> <a href="#informations" class="nav-tab"><?php _e( 'Informations', 'textdomain' ); ?></a> </h2> <form action="options.php" method="post"> <div id="first-options" class="tabs"> <?php do_settings_fields( __FILE__, 'first-options' ); ?> </div> <div id="second-options" class="tabs"> <?php do_settings_fields( __FILE__, 'second-options' ); ?> </div> <div id="informations" class="tabs"> … display informations … </div> <?php submit_button(); ?> </form> </div> <?php } ?> |
Il faut ensuite animer cela avec un script :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// Options page tabs jQuery( document ).ready( function( $ ) { // Tabs var $navs = $( '.nav-tab' ), $tabs = $( '.tabs' ), toogle = function( hash ) { location.hash = hash || ''; var hash = hash || $( 'a', $navs ).context[0].hash; $navs.removeClass( 'nav-tab-active' ); var $a = hash ? $( 'a[href="' + hash + '"]' ) : $( 'a:first-child', $navs ); $a.addClass( 'nav-tab-active' ); $tabs.hide(); $( hash ).show(); }; toogle( window.location.hash ); $navs.on( 'click', function( e ) { e.preventDefault(); var hash = e.target.hash; toogle( hash ); history.replaceState( {page: hash}, 'title ' + hash, hash ); }); }); |
La navigation se fait alors sans recharger la page, la barre d’adresse contient l’id de la div à afficher et le bouton submit_button() soumet toutes les options de tous les onglets.
Que l’on aura intégrer à la page d’options :
1 2 3 4 5 6 7 8 9 10 11 12 |
define( 'THEPLUGIN_URL', plugin_dir_url( __FILE__ ) ); // Backend Style/Script add_action( 'admin_enqueue_scripts', 'galettewp_admin_style' ); function galettewp_admin_style() { wp_enqueue_style( 'galettewp-backend-css', THEPLUGIN_URL . 'css/backend.css', array(), null ); $page_id = get_current_screen()->id; if( $page_id == 'toplevel_page_galettewp' ) { wp_enqueue_style( 'galettewp-css', THEPLUGIN_URL . 'css/galettewp.css', array(), null ); wp_enqueue_script( 'galettewp-js', THEPLUGIN_URL . 'js/galettewp.js', array( 'jquery' ), null, true ); } } |
Et hop, une jolie page d’options avec plein de réglages et une navigation optimisée pour l’utilisateur.
Mise à jour
Depuis WP 5.5, cela ne fonctionne plus vraiment bien, voir les commentaires ci-après. J’ai donc changé les liens sur les onglets. $menu_slug est le quatrième paramètre de la fonction add_menu_page() , l' url de la page d’options sera de la forme : http(s)://mydomain.tld/wp-admin/admin.php?page=$menu_slug .
On retrouve cette url avec la fonction menu_page_url( $menu_slug, false ) . L’argument false permet de ne pas imprimer le résultat de la fonction.
On écrit donc les onglets de la façon suivante, en ajoutant le paramètre tab .
1 2 3 4 5 6 7 8 |
<?php $url = menu_page_url( $menu_slug, false ); ?> <h2 class="nav-tab-wrapper"> <?php foreach( $tabs as $id => $tab ) { $active = $active_tab == $id ? 'nav-tab-active' : ''; echo "<a id=\"{$id}\" href=\"{$url}&tab={$id}\" class=\"nav-tab {$active} {$tab['icon']}\">{$tab['title']}</a>\n"; } ?> </h2> |
En ayant défini un tableau $tabs
pour les différents onglets :
1 2 3 4 5 6 7 8 9 10 |
$tabs = [ 'options1' => [ 'title' => __( 'Options 1', 'textdomain' ), 'icon' => 'icon-cog-gear', ], 'options2' => [ 'title' => __( 'Options 2', 'textdomain' ), 'icon' => 'icon-settings', ], ]; |
On définit ensuite le contenu des onglets :
1 2 3 4 5 6 |
<?php foreach( $tabs as $id => $tab ): ?> <div id="<?php echo $id; ?>" class="tabs"> <!-- <h2><?php echo $tab['title']; ?></h2> --> <?php $id(); ?> </div> <?php endforeach; ?> |
Les fonctions options1()
et options2()
écrivent le code HTML de chaque onglet.
Javascript
Ensuite, sur la page d’options, on change l' url dans la barre d’adresse du navigateur avec history.replaceState() pour ajouter le paramètre &tab= avec l' id de l’onglet. Le javascript devient donc :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
/* * js admin * Clio Manager Plugin */ jQuery( document ).ready( function( $ ) { // Tabs, navi const $navs = $( '.nav-tab' ), $tabs = $( '.tabs' ); const toggle = function( tab ) { // Navigation $tabs.hide().filter(function() {return this.id == tab}).show(); $navs.removeClass('nav-tab-active').filter(function() { return this.id == tab; }).addClass('nav-tab-active'); // history.pushState let href = location.href.replace(/&tab=.+$/, ''); let url = href + '&tab=' + tab; let title = tab.toUpperCase(); history.replaceState({tab: tab, href: href}, title, url); }; let url = new URL(location.href); let tab = url.searchParams.get('tab') || $navs.first().prop('id'); toggle( tab ); $navs.on( 'click', function( e ) { e.preventDefault(); tab = this.id; toggle( tab ); }); window.onpopstate = function( e ) { if( e.state != null ) { $('#' + e.state.tab).trigger( 'click'); } else { history.back(); } }; }); |
Je laisse window.onpopstate si l’on veut entrer la visite dans les différents onglets à l’historique du navigateur, cela se fait avec history.pushState(), que l’on doit mettre à la place de history.replaceState(). Ainsi, en cliquant sur la flèche de page précédente du navigateur, on retrouvera les onglets visités..
Version en pure javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/** * Script for options page tabs */ document.addEventListener('DOMContentLoaded', () => { // Tabs const navs = document.querySelectorAll('.nav-tab'), tabs = document.querySelectorAll('.tabs'); const toggle = tab => { // Navigation tabs.forEach((el, index) => { if(el.id == tab) { navs[index].classList.add('nav-tab-active'); el.style.display = 'block'; } else { navs[index].classList.remove('nav-tab-active'); el.style.display = 'none'; } }); // // history.replaceState const href = location.href.replace(/&tab=.+$/, ''); const url = href + '&tab=' + tab; const title = tab.toUpperCase(); history.replaceState({tab: tab, href: href}, title, url); }; const url = new URL(location.href); var tab = url.searchParams.get('tab') || navs[0].id; toggle(tab); navs.forEach(el => { el.addEventListener('click', e => { e.preventDefault(); tab = el.id; toggle(tab); }); }); window.onpopstate = e => { if (e.state != null) { document.getElementById(e.state.tab).click(); } else { history.back(); } }; }); |
Version minifiée
Une version minifiée, en ayant changé le nom de la fonction toggle
en switch_toggle
, sans écouteur DOMContentLoaded
:
1 |
const navs=document.querySelectorAll(".nav-tab"),tabs=document.querySelectorAll(".tabs"),switch_toggle=t=>{tabs.forEach((a,e)=>{a.id==t?(navs[e].classList.add("nav-tab-active"),a.style.display="block"):(navs[e].classList.remove("nav-tab-active"),a.style.display="none")});const a=location.href.replace(/&tab=.+$/,""),e=a+"&tab="+t,s=t.toUpperCase();history.replaceState({tab:t,href:a},s,e)},url=new URL(location.href);var tab=url.searchParams.get("tab")||navs[0].id;switch_toggle(tab),navs.forEach(t=>{t.addEventListener("click",a=>{a.preventDefault(),tab=t.id,switch_toggle(tab)})}),window.onpopstate=(t=>{null!=t.state?document.getElementById(t.state.tab).click():history.back()}); |
Et hop, il y a certainement des améliorations à apporter, mais cela semble marcher assez bien.
la ligne
var hash = hash || $( ‘a’, $navs ).context[0].hash;
ne semble plus marcher avec WordPress 5.5 , on peut la remplacer par
Exact, et le lien avec l’id de l’onglet non plus. Je vais mettre à jour en gardant le chargement de tous les onglets (plus rapide ensuite pour la navigation), mais en ajoutant un paramètre page pour gérer les différents onglets. Il faudra aussi voir la sauvegarde des options, si l’on fait cela en Ajax, ou en rechargeant la page.
Merci pour votre suggestion.