Adding Custom Attributes To WordPress Menus

They are so many tutorials on the web to explain “how to add a custom field to WordPress menus” but most of them treat about how to use the default CSS field of the default Description field. None of them gives a real method to add a real new field to menus. So, today we will create a simple plugin that will add a “subtitle” field to any WordPress menu.

Here is what the final result is in the WordPress administration:

Step 1: The Plugin Creation

Let’s go, as we saw in one of my previous post, creating a plugin isn’t complex. You just need to create a new folder under wp-content/plugins and called it “sweet-custom-menu”, and to create inside of it a file called “sweet-custom-menu.php”. Then open this file and add this code:

<?php
/*
Plugin Name: Sweet Custom Menu
Plugin URL: http://remicorson.com/sweet-custom-menu
Description: A little plugin to add attributes to WordPress menus
Version: 0.1
Author: Remi Corson
Author URI: http://remicorson.com
Contributors: corsonr
Text Domain: rc_scm
Domain Path: languages
*/

This code is all we need to define a new plugin. Now we are going to create a custom PHP class containing a constructor and the functions we will need to make the plugin work well.

Here is how to create the class:

class rc_sweet_custom_menu {

	/*--------------------------------------------*
	 * Constructor
	 *--------------------------------------------*/

	/**
	 * Initializes the plugin by setting localization, filters, and administration functions.
	 */
	function __construct() {

	} // end constructor

	/* All functions will be placed here */

}

// instantiate plugin's class
$GLOBALS['sweet_custom_menu'] = new rc_sweet_custom_menu();

The last line instantiates the class and add the whole class in a global variable.

Step 2: Adding Custom Fields Filter

Now that we have our class, we are going to create custom functions. The first function to add is the one that will register the “sub-title” custom field we want to be included as an advanced menu attribute. To do so, here the function to create, place this code instead of /* All functions will be placed here */ :

/**
 * Add custom fields to $item nav object
 * in order to be used in custom Walker
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
function rc_scm_add_custom_nav_fields( $menu_item ) {

    $menu_item->subtitle = get_post_meta( $menu_item->ID, '_menu_item_subtitle', true );
    return $menu_item;

}

Then, we need to tell WordPress to take our function into account, so place the code below in the __construct() function:

// add custom menu fields to menu
add_filter( 'wp_setup_nav_menu_item', array( $this, 'rc_scm_add_custom_nav_fields' ) );

Step 3: Saving Custom Fields

Even if the custom “sub-title” field doesn”t really exist we must create a function that will save its value on menu edition. Menu elements are in two wards custom post types, so we can use the custom post type APIs and post_meta method. So, to save custom menu field value, add this function under rc_scm_add_custom_nav_fields():

/**
 * Save menu custom fields
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
function rc_scm_update_custom_nav_fields( $menu_id, $menu_item_db_id, $args ) {

    // Check if element is properly sent
    if ( is_array( $_REQUEST['menu-item-subtitle']) ) {
        $subtitle_value = $_REQUEST['menu-item-subtitle'][$menu_item_db_id];
        update_post_meta( $menu_item_db_id, '_menu_item_subtitle', $subtitle_value );
    }

}

In this function, we are checking if the custom field value is sent from the form we ar about to create and then we simply store its value. Now we have to add the function to the appropriate hook. To do so, add this lines to __construct():

// save menu custom fields
add_action( 'wp_update_nav_menu_item', array( $this, 'rc_scm_update_custom_nav_fields'), 10, 3 );

Step 4: The Form

If you followed this tutorial step by step, you’ll probably guess that we haven’t created the form that must contain our sub-title field. This part is a bit more complex than the previous ones. This time we have to deal with Walker. I really encourage you to read the codex about the walker class, this will really help you to understand what it is and what it does. By the way they’re many great tutorials everywhere on the internet to give you more details about this class, so please check them out! In most part of the time, walkers are used to modify the HTML output of a menu. Here, we are working on the admin menu output form. Simply add this function to your main class:

/**
 * Define new Walker edit
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
function rc_scm_edit_walker($walker,$menu_id) {

    return 'Walker_Nav_Menu_Edit_Custom';

}

and then this to the constructor:

// edit menu walker
add_filter( 'wp_edit_nav_menu_walker', array( $this, 'rc_scm_edit_walker'), 10, 2 );

What this does is replacing the default admin edit menu form by a custom one. Now that the filter, is added, copy these two lines at the bottom of the sweet-custom-menu.php file, outside the rc_sweet_custom_menu class:

include_once( 'edit_custom_walker.php' );
include_once( 'custom_walker.php' );

We are about to include two files. The first one “edit_custom_walker.php” is the one that will modify the default form to edit the menu. It’s in this file that we are going to add the sub-title field.

The second one is the welker used on the website frontend, this is the file that will modify the menu output to your visitors.

As the “edit_custom_walker.php” is a bit long, i am not going to paste the whole code. You can view the whole code here. The only code i added to it is from line 174 to line 185. Here is the added code:

<p class="field-custom description description-wide">
<label for="edit-menu-item-subtitle-<?php echo $item_id; ?>">
<?php _e( 'Subtitle' ); ?><br />
<input type="text" id="edit-menu-item-subtitle-<?php echo $item_id; ?>" class="widefat code edit-menu-item-custom" name="menu-item-subtitle[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $item->subtitle ); ?>" />
</label>
</p>

If you want to add some more fields to the menu, simply duplicate these lines and copy & paste them. Once this step is done, the menu should be working in the administration. You should now be able to see the new “sub-title” field when adding a new element to a menu. If it’s not the case, make you followed the tutorial step by step. It’s now time time to output the subtitle value on the frontend.

Step 5: Custom Field Output

If everything is working fine in the administration, you probably now want to display the subtitle on the frontend. Open custom_walker.php and add thise code ot it:

<?php
/**
 * Custom Walker
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
class rc_scm_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        ) .'"' : '';
           $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

           $prepend = '<strong>';
           $append = '</strong>';
           $description  = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';

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

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

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

This code is pretty standard. The only important thing here is these line:

$item_output .= ' '.$item->subtitle.'';

Step 6: Call The Walker!

In the last step you will need to open the header.php file in your theme folder. It’s where most part of the time the menu is called. If not, then contact the theme creator to know where it’s called.

Find the wp_nav_menu() function.

Then simply add the walker parameter:

'walker' => new rc_scm_walker

That’s it ! I hope you enjoyed adding a custom field to a WordPress menu. You can now modify a bit this code to add other fields such as checkboxes, select dropdown, textareas etc… Feel free to add a comment in the comments section. If you found a bug, if you improve it etc…

Sweet Custom Menu Plugin

If you are looking to implement this tutorial you can download the “Sweet Custom Menu Plugin” I’ve created to get you started. Enjoy!

Get The Plugin →

Post Author: Remi

Remi is an expert WordPress developer that coded many great free and premium themes and plugins. His experience on WordPress allows him to produce great stuff and to propose advanced tutorials.

Disclosure: This page contains external affiliate links that may result in us receiving a comission if you choose to purchase said product. The opinions on this page are our own. We do not receive payment for positive reviews.
Got something to say? Join the discussion.
  1. Remi, This is awesome! I had no idea you could do this. Thanks for the great tutorial.
    • Thanks Gregg, this tutorial is just a simple example, but you could do much more using this method. I'm currently working on an awesome plugin related to WordPress menus that should be really handful!
      Author
      • AJ Clarke | WPExplorer says:
        AJ Clarke | WPExplorer
        Can't wait to see what you're working on!
        Admin
        • All i can say is that i am doing it in parntership with the codecanyon top seller!!!
  2. Sounds good but missing a showcase. And got a question: Could I add in this way some more targets (_top) in menu link?
    • Hi Mark, i sent the plugin to the WordPress official repo, so you can downolad and use it easily. And to answer your question, yes you can use this method to add links target (even id wordpress gives you by default a checkbox to open a link in a new window)
      Author
  3. I tried this using a twenty-twelve child theme and get this error when I add the walker parameter to headers.php: Fatal error: Using $this when not in object context in .../wp-includes/class-wp-walker.php on line 185 I'm pretty sure I followed the instructions that came with the plugin correctly.
    • jumbowei says:
      I recently ran into the same problem and could fix it. You have to use wp_nav_menu(array('menu' => 'main', 'walker' => new rc_scm_walker));
  4. Hi,Remi I got these notice after using debug bar plugin : Trying to get property of non-object online : 39 41 42 44 please help please. Best regards, Decneo
  5. This might be asking a lot, but this is the closest I've come across in my research so far. I'm looking to add a data-reveal-id="" field to my menu items (specifically one of them) to trigger a PopPop! (http://wordpress.org/extend/plugins/poppop/) modal window. I added the additional section to the edit_custom_walker.php file like you recommended: <label for="edit-menu-item-subtitle-"> <input type="text" id="edit-menu-item-subtitle-" class="widefat code edit-menu-item-custom" name="menu-item-subtitle[]" value="data-reveal-id ); ?>" /> And the attribute item appears in my Menu screen now, but I'm not sure how to modify this part to ensure that it actually outputs the field's contents into the front-end of my site: $item_output .= ' '.$item->subtitle.''; Also, currently, whatever I type in the field reverts to a value of 0 when I save and replaces the subtitle value. Could be related to the above unmodified section, but I thought I'd post that too, in case it was indicative of me missing something else along the way. Thanks in advance if you get the opportunity to answer this. - Steven
    • Never mind, I just used a 'JQuery var newHTML =' function to find and replace the menu item in question.
      • AJ Clarke | WPExplorer says:
        AJ Clarke | WPExplorer
        Sorry no one ever got back to you for the initial comment! Glad you got it working though ;)
        Admin
    • Rami Girgis says:
      I think it's easier to edit the custom_walker.php file and remove $item_output .= ‘ ‘.$item->subtitle.”; (make sure to leave the close in) and then add a new line for $attributes above that (e.g. copy and paste $attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : ''; and then change xfn (two instances) to subtitle, and rel to data-reveal-id. Then in edit_custom_walker.php you can change the label "Subtitle" to "Data Reveal" or whatever you want, so that the label is appropriate in the form in the wp admin menu section
  6. The plugin is not working. Like already said above it gives an error: Fatal error: Using $this when not in object context in /wp-includes/class-wp-walker.php on line 185. Do you have a working version?
  7. Just to let other know, the description at http://wordpress.org/extend/plugins/sweet-custom-menu/installation/ is wrong, you should define the walker in wp_nav_menu like this: 'walker' => new rc_scm_walker
    • Hi Alex, thanks for mentioning the error. You're totally true. I have to update the description.
      Author
  8. awesome! now I can have everything I want on my menu
  9. Fernando says:
    I´m writting 'walker' => 'rc_scm_walker' and I get the error: Using $this when not in object context in /wp-includes/class-wp-walker.php on line 185. What I need to write or to change to make functional this plugin?
    • Rami Girgis says:
      Make sure you are using the same type of single quotes that are already being used in the other values/keys in the array, and then change 'walker' => 'rc_scm_walker' to 'walker' => new rc_scm_walker
  10. Ciprian Tepes (@cippo_ro) says:
    Thank you very much for your post! It really helped me out to build a color picker for each menu item. Cheers!
    • AJ Clarke | WPExplorer says:
      AJ Clarke | WPExplorer
      Nice! Would love to see what you came up with, is this for a theme/plugin available somewhere?
      Admin
  11. Native Imaging says:
    ‘walker’ => new rc_scm_walker" is causing a parse error.. Can you post the code intact inside the wp_nav_menu() ?
  12. Rami Girgis says:
    Navtive Imaging: you are using the wrong type of quotes (not your fault, the code in the tutorials is using the wrong type). Make sure you manually type it in instead of copying and pasting. Or copy and paste an existing key/value from the array and just change the values to walker and new rc_scm_walker.
    • AJ Clarke | WPExplorer says:
      AJ Clarke | WPExplorer
      The issue is that when we updated to 3.6 recently it appears our syntax highlighter has stopped working. I'll get that fixed up.
      Admin
  13. ozenyxozzy says:
    Try:
    $walker = new rc_scm_walker;
    and add that as
    'walker' => $walker, 
    This sholt fix it
  14. onilinkcr says:
    This may be a dumb question but can you call TWO Walkers or not? I mean one would be for your tutorial and the other I need for a description of the menu item. Possible?
  15. Hi, I have been trying to add a custom checkbox but it isn't working, any tips creating the custom meta or updating the custom meta for a checkbox would be awesome. Thanks in advance
  16. Its nice to to add sub-menu field , same way we can add thumbnail-image url input field to menu items , As we know on menu edit admin page ,we mostly shuffle the position of items by sorting menu list. so parent menu ,sub-menu relation changes on every instance of sorting. so how can we add custom field to top-level items only and not the sub-menu items.
    • AJ Clarke says:
      AJ Clarke
      Hi Vivin, I'm not actually sure how to do that, but if you find out, please let me know! That sounds really handy.
      Admin
  17. Matt Cromwell says:
    Thanks for a great write-up. I used this tutorial to create an enahanced "Target" field that uses a dropdown to target FooBox from the menu. I also updated the code to use WP 4.0 Walker class. Check it out here: https://github.com/mathetos/target-foobox
  18. Olof Larsson says:
    I used this instead of rewriting all that html, I haven't tested it yet, but I hope it works :) Cheers!
    &lt;?php
    include_once( ABSPATH . &#039;wp-admin/includes/nav-menu.php&#039; );
    
    class CustomAdminMenuWalker extends Walker_Nav_Menu_Edit{
    	public function start_el( &amp;$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    		parent::start_el($output, $item, $depth, $args, $id);
    		$needle = &#039;';
    		$pos = strrpos($output, (string)$needle);
    		$start = substr($output, 0, $pos-1);
    		$end = substr($output, $pos-1, strlen($output)-1);
    
    		$output = $start.$this-&gt;addCustomFields().$end;
    	}
    	public function addCustomFields(){
    		return "Baaam";
    	}
    }
  19. Can use this in my theme in commercial use?
    • AJ Clarke says:
      AJ Clarke
      Of course, this is a tutorial - do as you wish with the code ;)
      Admin
  20. Hello, thanks for this great plugin but I think it is not compatible with wordpress 4.1 because I got a fatal error at front end after installing this plugin. Can you fix it please? I downloaded the plugin from wordpress.org. thanks
  21. Bareera Ahmed says:
    Hi, Thank you for this tip, very good. I would like to know if there's any way to repeat this way to make more menus? I tried but it's bring me an error. Thanks in advance!
  22. Please fix the code.: 'walker' => 'rc_scm_walker' is supposed to be 'walker' => new rc_scm_walker
  23. "As the “edit_custom_walker.php” is a bit long, i am not going to paste the whole code. You can download the file here." Sorry, where can I download it?
    • AJ Clarke says:
      AJ Clarke
      I updated the link, thanks! The plugin is on WordPress.org for you to download.
      Admin
  24. meysam98 says:
    Hi and thanks for this, i am using wp_bootstrap_navwalker in my theme and this code have conflict. Please help me, how i solve it.
    • AJ Clarke says:
      AJ Clarke
      That's because you are already using a walker "wp_bootstrap_navwalker" so following this guide won't work. You will have to instead modify the existing "wp_bootstrap_navwalker" to work with your custom attributes.
      Admin

Leave a Reply