0

I've originally hard-coded menu links into the mega menu of mandoemedia.com in order to achieve the icon and description of each link.

enter image description here

I'm wondering if it is possible to convert these submenus into menus I can add through Appearance > Menus while still maintaining the icons and descriptions.

I see through Screen Options I can add a Description to each menu item and a CSS Class which I can use as in image filename.

From this page, I've created a custom plugin:

<?php
/*
Plugin Name: Mandoe Mega Menu Helper
Description: Allows images and descriptions in mega menus
Author: Steve Doig
Version: 1.0.0
*/

class mandoe_menu_walker extends Walker {
    function start_el(&$output, $item, $depth = 0, $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 .'>';

        $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 class="mm-mm-subtext">'.esc_attr( $item->description ).'</span>' : '';

        if($depth != 0) {
            $description = $append = $prepend = "";
        }

        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'><img src="' . get_stylesheet_directory_uri() . '/img/icon-' . $class_names .'" alt="' . $item->title . '" class="mm-mm-icon">';
        $item_output .= $args->link_before .$prepend.apply_filters( 'the_title', $item->title, $item->ID ).$append;
        $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 );
    }
}

?>

but when activating this plugin I receive an error:

Fatal error: Declaration of mandoe_menu_walker::start_el(&$output, $item, $depth, $args) must be compatible with Walker::start_el(&$output, $data_object, $depth = 0, $args = [], $current_object_id = 0) in /wp-content/plugins/mm-mega-menu-helper/mm-mega-menu-helper.php on line 10

Help appreciated.

2 Answers 2

1

An entirely different approach.

nav menu item are stored in posts table, so you can technically attach a post_meta into it, even other data like nav item class already stored in post mata table. However this wasn't easy to do before.

but after 5.4.0 release, they have implemented hooks to easily do this.

Here's the full step

Add Custom Meta box on Nav item

add_action( 'wp_nav_menu_item_custom_fields', function( $id, $navItem) {

  wp_nonce_field( 'nav_menu_icon_nonce', '_nav_menu_icon_nonce_name' );
  $nav_menu_icon = get_post_meta( $id, '_nav_menu_icon', true );
  ?>
  <p class="field-nav-icon nav-icon description-wide">
    <label for="edit-menu-item-nav-icon-<?php echo $id ;?>">
      Nav Icon<br>
      <textarea id="edit-menu-item-nav-icon-<?php echo $id ;?>" class="widefat edit-menu-item-nav-icon" rows="3" cols="20" name="menu_item_nav_icon[<?php echo $id ;?>]"><?php echo esc_attr( $nav_menu_icon ); ?></textarea>
      <span class="description">Add icon on menu.</span>
    </label>
  </p>
  <?php
}, 10, 2 );

There should be another box in nav item after adding the code above like this enter image description here

Then save the value of that field into post meta

add_action( 'wp_update_nav_menu_item', function($menuId, $menuItemId) {

  // current_user_can( 'unfiltered_html' ) is a wordpress role which ensure the user can post unfiltered html
  if ( ! isset( $_POST['_nav_menu_icon_nonce_name'] ) || ! wp_verify_nonce( $_POST['_nav_menu_icon_nonce_name'], 'nav_menu_icon_nonce' ) || !current_user_can( 'unfiltered_html' ) ) 
    return $menuId;
    
  if ( isset( $_POST['menu_item_nav_icon'][$menuItemId]  ) ) {
    update_post_meta( $menuItemId, '_nav_menu_icon', $_POST['menu_item_nav_icon'][$menuItemId] );
  } else {
    delete_post_meta( $menuItemId, '_nav_menu_icon' );
  }
}, 10, 2 );

and thats it, you now have additional data connected to each menu item for nav icon that supports text and html, you can add html tag like <img /> for adding image as menu icon, or svg,

To display on the front-end, this depends on your set-up and where you want them to show

an example below that modify the nav menu item title via nav_menu_item_title filter and include the icon using get_post_meta to pull the icon value

add_filter( 'nav_menu_item_title', function( $title, $item) {
  
  if( is_object( $item ) && isset( $item->ID ) ) {
    
    $navIcon = get_post_meta( $item->ID, '_nav_menu_icon', true );
    
    if ( ! empty( $navIcon ) ) {
      $title = '<span class="icon">'.$navIcon.'</span>' . $title;
    }
    
  }
  return $title;
  
}, 10, 2 );

and the title would show the svg icon if there is

enter image description here

0

I changed the function definition to

function start_el(&$output, $item, $depth = 0, $args=[], current_object_id = 0) 

and it activated okay.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.