How to Save Nav Menu Items Individually (instead of saving the whole menu)

Do not want to save the whole menu each time you need to edit one or two elements of it?

#navigation, #wp-admin  /  August 19  /   8

First of all let me show what I mean:

How to edit each menu item in WordPress individually

When do we need this functionality?

  • When there are a lot of menu items and we need to change some of them.
  • When we have very slow internet connection or slow server response time.

Step 1. Create JS Script and Enqueue it into WP Admin

Okay lets create an empty javascript file in your theme directory (for example themes/twentytwelve-child/menuitems.js).

function rudr_include_menu_script() {
 	wp_enqueue_script( 'menuitems', get_stylesheet_directory_uri() . '/menuitems.js', array('jquery'), null, false );
}
 
add_action( 'admin_enqueue_scripts', 'rudr_include_menu_script' );

Step 2. Live Query jQuery plugin

We need this plugin to add the «Edit» button for each nav menu item.

Why not use each() function? Because this functions work for all existing elements only. We need edit button for new elements also.

You can download it here and enqueue it using wp_enqueue_script function or just insert the plugin code at the beginning of your menuitems.js.

Step 3. Add Edit Buttons and Configure Events for them

Please, insert the code below to your menuitems.js. It adds «Edit» button for every menu element in wp-admin/nav-menus.php and allows us to send ajax requests by clicking this button.

/* here is the place where you can insert Live Query plugin */
 
jQuery(function($){
 
	$(".menu-item-settings").livequery(function() {
		$(this).append('<a class="button button-primary rudr-menu-save">Edit</a>');
	});
 
	// if you do not want to use Live Query plugin, then comment the lines above and uncomment the lines below
	//$('.menu-item-settings').each(function(i){ 
	//	$(this).append('<a class="button button-primary rudr-menu-save">Edit</a>');
	//});
 
	$('.rudr-menu-save').live('click',function(){
		button = $(this);
		el = button.parent();
		var url = el.find('.edit-menu-item-url').val();
		var attrtitle = el.find('.edit-menu-item-attr-title').val();
		var title = el.find('.edit-menu-item-title').val();
		var classes = el.find('.edit-menu-item-classes').val();
		var xfn = el.find('.edit-menu-item-xfn').val();
		var desc = el.find('.edit-menu-item-description').val();
		var target = el.find('.field-link-target').find('input').attr('checked');
		var menuid =  el.find('.menu-item-data-db-id').val();
		var parentid = el.find('.menu-item-data-parent-id').val();
		var menu = $('#menu').val();
		var pos = el.find('.menu-item-data-position').val();
		$.ajax({
			type: 'POST',
			url: ajaxurl,
			data: 'action=saveitem&url=' + url + '&attrtitle=' + attrtitle + '&title=' + title + '&classes=' + classes + '&xfn=' + xfn + '&desc=' + desc + '&target=' + target + '&menuid=' + menuid + '&parentid=' + parentid + '&menu=' + menu + '&pos=' + pos + '&max=' + $('.menu-item-settings').length,
			beforeSend: function(xhr){
				button.text('Saving...');
			},
			success: function(data){
				button.text('Edit');
			}
		});
		return false;
	});
 
});

Step 4. Update Menu Element

The PHP code below receive our ajax requests and make the changes with menu elements. You can insert it into your theme functions.php.

function rudr_saveitem_callback(){
 
	// parameters for wp_insert_post() function (yes, menu elements are the custom post types)
	$my_postarr = array(
		'ID'		=> $_POST['menuid'],
		'post_title'    => $_POST['title'],
		'post_type'	=> 'nav_menu_item',
		'post_status'	=> 'publish',
		//'menu_order'	=> $_POST['pos'], // I comment this line because the code doesn't work properly with menu item position
		'post_content'  => $_POST['desc'], // description
		'post_excerpt'   => $_POST['attrtitle'] // title attribute
	);
 
	// we should set the right position for new menu items
 	if ( 'draft' == get_post_status( intval($_POST['menuid']) ) ) {
 		$my_postarr['menu_order'] = $_POST['max'];
 	}
 
	// insert or update the menu element
	$my_post_id = wp_insert_post( $my_postarr );
 
	// set the right menu for it (yes, menus are taxonomies)
	wp_set_object_terms( $my_post_id, intval($_POST['menu']), 'nav_menu', $append = false );
 
	// post meta parameters
	$target = ($_POST['target'] == 'checked') ? '_blank' : ''; 
	update_post_meta( $my_post_id, '_menu_item_target', $target);
	update_post_meta( $my_post_id, '_menu_item_menu_item_parent', $_POST['parentid']);
	update_post_meta( $my_post_id, '_menu_item_classes', $_POST['classes']);
	update_post_meta( $my_post_id, '_menu_item_xfn', esc_attr($_POST['xfn']));
	update_post_meta( $my_post_id, '_menu_item_url', esc_attr($_POST['url']));
 
	die();
}
 
if( is_admin() ) {
	add_action('wp_ajax_saveitem', 'rudr_saveitem_callback');
}

If you have a question or an idea how to improve this code, please, leave it in comments.

Only the best of WordPress

Subscribe to this weekly newsletter to receive the latest blog posts by email.I respect your privacy. Your email is safe with me.

Comments 8

  • Edit Button is not showing..Amit

  • Hi! this is an excellent work you have done thank you..!

    You missed for removing menu items but i have done it use it Guys !
    Put Bellow code in menuitems.js

    $("a.item-delete").addClass('custom-remove-item');
    $("a.custom-remove-item").removeClass('item-delete');
     
    $('.custom-remove-item').live('click',function(){
    		button = $(this);
    		delid = button.attr('id');
    		ajaxdelurl = button.attr('href');
    		var itemID = parseInt(button.attr('id').replace('delete-', ''), 10);
    		button.addClass('item-delete');
    		$.ajax({
    			type: 'GET',
    			url: ajaxdelurl,
    			beforeSend: function(xhr){
    				button.text('Removing...');
    			},
    			success: function(data){
    				button.text('Remove');
    				$("#"+delid).trigger("click");
    			}
    		});
    		return false;
    	});
  • Hi,
    Thank you so much for this code.
    I use it successfully but there is 1 problem you noted yourself in line 9 in
    function rudr_saveitem_callback(){

    'menu_order'	=> $_POST['pos'], // I comment this line because the code doesn't work properly with menu item position

    Even with this line commented I still get the order messed up.
    Did you ever find a solution for that?

    Thank you very much.
    Y

    • Hi,
      I hope to find the time to resolve this on the next week.

    • Hi again,
      I found out that if you want to change the items order you need to update all the menu elements. To do this you need:

      1) to send the elements order in AJAX request
      2) to loop through all the elements and save menu_order for each

  • Great! I try too, will let you you know if I find something.
    Thanks.
    Y

Leave your question or feedback

phpjsHTMLCSSSQLCode
Please, enter a comment
Please, enter a name
Incorrect email