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
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