WordPress – menù di navigazione in stile Bootstrap 4.0

 4 Gennaio 2018


Adattare il menù di navigazione generato da WordPress a Bootstrap 4.0 sarà un gioco da ragazzi grazie a poche righe di codice. Il tuo sito è sviluppato usando Bootstrap 3? Nessun problema, c’è anche la versione retrocompatibile!

Ogni sviluppatore web che si rispetti avrà avuto a che fare con lo sviluppo di temi per WordPress. Io personalmente ne avrò sviluppati una cinquantina, e ho deciso di condividere in questo post uno gli snippet più utili da utilizzare nei temi che permette di adattare il menù di navigazione di WordPress affinché risulti compatibile con il formato richiesto dal framework CSS Bootstrap 4.0.

Per iniziare, abbiamo bisogno dello script wp_bootstrap_navwalker disponibile qui. Scaricatelo, e inseritelo nella cartella del vostro tema, rinominando il file in wp_bootstrap_navwalker.php.

Attenzione: la versione scaricata è compatibile solo con Bootstrap 3. Di seguito il codice aggiornato compatbile con la versione 4.0.

<?php
/**
 * Class Name: wp_bootstrap_navwalker
 * GitHub URI: https://github.com/twittem/wp-bootstrap-navwalker
 * Description: A custom WordPress nav walker class to implement the Bootstrap 3 navigation style in a custom theme using the WordPress built in menu manager.
 * Version: 2.0.4
 * Author: Edward McIntyre - @twittem
 * License: GPL-2.0+
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 */
class wp_bootstrap_navwalker extends Walker_Nav_Menu {
 /**
 * @see Walker::start_lvl()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param int $depth Depth of page. Used for padding.
 */
 public function start_lvl(&$output, $depth = 0, $args = array()) {
 $indent = str_repeat("\t", $depth);
 $output .= "\n$indent<ul role=\"menu\" class=\" dropdown-menu\">\n";
 }
 /**
 * @see Walker::start_el()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param object $item Menu item data object.
 * @param int $depth Depth of menu item. Used for padding.
 * @param int $current_page Menu item ID.
 * @param object $args
 */
 public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
 $indent = ($depth) ? str_repeat("\t", $depth) : '';
 /**
 * Dividers, Headers or Disabled
 * =============================
 * Determine whether the item is a Divider, Header, Disabled or regular
 * menu item. To prevent errors we use the strcasecmp() function to so a
 * comparison that is not case sensitive. The strcasecmp() function returns
 * a 0 if the strings are equal.
 */
 if (strcasecmp($item->attr_title, 'divider') == 0 && $depth === 1) {
 $output .= $indent . '<li role="presentation" class="divider">';
 } else if (strcasecmp($item->title, 'divider') == 0 && $depth === 1) {
 $output .= $indent . '<li role="presentation" class="divider">';
 } else if (strcasecmp($item->attr_title, 'dropdown-header') == 0 && $depth === 1) {
 $output .= $indent . '<li role="presentation" class="dropdown-header">' . esc_attr($item->title);
 } else if (strcasecmp($item->attr_title, 'disabled') == 0) {
 $output .= $indent . '<li role="presentation" class="disabled"><a href="#">' . esc_attr($item->title) . '</a>';
 } else {
 $class_names = $value = '';
 $classes = empty($item->classes) ? array() : (array) $item->classes;
 $classes[] = 'nav-item nav-item-' . $item->ID;
 $class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item, $args));
 if ($args->has_children)
 $class_names .= ' nav-item dropdown';
 if (in_array('current-menu-item', $classes))
 $class_names .= ' active';
 $class_names = $class_names ? ' class="' . esc_attr($class_names) . '"' : '';
 $id = apply_filters('nav_menu_item_id', 'nav-item nav-item-' . $item->ID, $item, $args);
 $id = $id ? ' id="' . esc_attr($id) . '"' : '';
 $output .= $indent . '<li' . $id . $value . $class_names . '>';
 $atts = array();
 $atts['title'] = !empty($item->title) ? $item->title : '';
 $atts['target'] = !empty($item->target) ? $item->target : '';
 $atts['rel'] = !empty($item->xfn) ? $item->xfn : '';
 // If item has_children add atts to a.
 if ($args->has_children && $depth === 0) {
 $atts['href'] = !empty($item->url) ? $item->url : '';
 $atts['data-toggle'] = 'dropdown';
 $atts['class'] = 'dropdown-toggle';
 $atts['aria-haspopup'] = 'true';
 } else {
 $atts['href'] = !empty($item->url) ? $item->url : '';
 }
 $atts = apply_filters('nav_menu_link_attributes', $atts, $item, $args);
 $attributes = '';
 foreach ($atts as $attr => $value) {
 if (!empty($value)) {
 $value = ('href' === $attr) ? esc_url($value) : esc_attr($value);
 $attributes .= ' ' . $attr . '="' . $value . '"';
 }
 }
 $item_output = $args->before;
 /*
 * Glyphicons
 * ===========
 * Since the the menu item is NOT a Divider or Header we check the see
 * if there is a value in the attr_title property. If the attr_title
 * property is NOT null we apply it as the class name for the glyphicon.
 */
 if (!empty($item->attr_title))
 $item_output .= '<a class="nav-link"' . $attributes . '><span class="glyphicon ' . esc_attr($item->attr_title) . '"></span>&nbsp;';
 else
 $item_output .= '<a class="nav-link" ' . $attributes . '>';
 $item_output .= $args->link_before . apply_filters('the_title', $item->title, $item->ID) . $args->link_after;
 $item_output .= ($args->has_children && 0 === $depth) ? ' <i class="fa fa-angle-down" aria-hidden="true"></i></a>' : '</a>';
 $item_output .= $args->after;
 $output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args);
 }
 }
 /**
 * Traverse elements to create list from elements.
 *
 * Display one element if the element doesn't have any children otherwise,
 * display the element and its children. Will only traverse up to the max
 * depth and no ignore elements under that depth.
 *
 * This method shouldn't be called directly, use the walk() method instead.
 *
 * @see Walker::start_el()
 * @since 2.5.0
 *
 * @param object $element Data object
 * @param array $children_elements List of elements to continue traversing.
 * @param int $max_depth Max depth to traverse.
 * @param int $depth Depth of current element.
 * @param array $args
 * @param string $output Passed by reference. Used to append additional content.
 * @return null Null on failure with no changes to parameters.
 */
 public function display_element($element, &$children_elements, $max_depth, $depth, $args, &$output) {
 if (!$element)
 return;
 $id_field = $this->db_fields['id'];
 // Display this element.
 if (is_object($args[0]))
 $args[0]->has_children = !empty($children_elements[$element->$id_field]);
 parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
 }
 /**
 * Menu Fallback
 * =============
 * If this function is assigned to the wp_nav_menu's fallback_cb variable
 * and a manu has not been assigned to the theme location in the WordPress
 * menu manager the function with display nothing to a non-logged in user,
 * and will add a link to the WordPress menu manager if logged in as an admin.
 *
 * @param array $args passed from the wp_nav_menu function.
 *
 */
 public static function fallback($args) {
 if (current_user_can('manage_options')) {
 extract($args);
 $fb_output = null;
 if ($container) {
 $fb_output = '<' . $container;
 if ($container_id)
 $fb_output .= ' id="' . $container_id . '"';
 if ($container_class)
 $fb_output .= ' class="' . $container_class . '"';
 $fb_output .= '>';
 }
 $fb_output .= '<ul';
 if ($menu_id)
 $fb_output .= ' id="' . $menu_id . '"';
 if ($menu_class)
 $fb_output .= ' class="' . $menu_class . '"';
 $fb_output .= '>';
 $fb_output .= '<li><a href="' . admin_url('nav-menus.php') . '">Add a menu</a></li>';
 $fb_output .= '</ul>';
 if ($container)
 $fb_output .= '</' . $container . '>';
 echo $fb_output;
 }
 }
}

Adesso occorrerà richiamare il file dal file functions.php del nostro tema. Per farlo, utilizzeremo una riga di codice:

require_once('wp_bootstrap_navwalker.php');

L’ultimo passaggio da eseguire sarà associare alla funzione wp_nav_menu di WordPress il walker appena definito. Per farlo, è necessario agire sul file del tema in cui il menù sarà posizionato. Nel mio caso sarà header.php. Il codice necessario per avere in output il risultato desiderato è:

<?php
 wp_nav_menu( array(
 'menu' => get_post_meta( $post->ID, 'MenuName', true),
 'theme_location' => 'top_menu',
 'depth' => 14,
 'container' => 'div',
 'container_class' => 'collapse navbar-collapse',
 'container_id' => 'navigazione',
 'menu_class' => 'navbar-nav mr-auto',
 'fallback_cb' => 'wp_bootstrap_navwalker::fallback',
 'walker' => new wp_bootstrap_navwalker())
 );
 ?>

Il codice sovrastante andrà inserito all’interno del tag <nav>, secondo le specifiche richieste da Bootstrap 4.0 disponibili qui.
Volendo essere più precisi quindi, il codice finalizzato sarà:

<nav class="nav navbar navbar-expand-lg">
 <div class="container">

<a class="navbar-brand" href="<?php echo home_url(); ?>" title="<?php bloginfo('name'); ?>">
 <img src="<?php echo get_template_directory_uri();?>/images/logo.png" alt="<?php bloginfo('name'); ?>" border="0">
 </a>

<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigazione" aria-controls="navigazione" aria-expanded="false" aria-label="Toggle navigation">
 <span class="navbar-toggler-icon"></span>
 </button>
<?php
 wp_nav_menu( array(
 'menu' => get_post_meta( $post->ID, 'MenuName', true),
 'theme_location' => 'top_menu',
 'depth' => 14,
 'container' => 'div',
 'container_class' => 'collapse navbar-collapse',
 'container_id' => 'navigazione',
 'menu_class' => 'navbar-nav mr-auto',
 'fallback_cb' => 'wp_bootstrap_navwalker::fallback',
 'walker' => new wp_bootstrap_navwalker())
 );
 ?>
 </div>
</nav>

Il gioco è fatto, il menù generato da WordPress rispecchierà la struttura richiesta da Bootstrap 4.0 e sarà quindi completamente responsive.


Lascia un commento