6

Does anybody have an idea on how to allow a user to drop in separators or dividers between certain nav menu items?

Google only seems to bring up rookie CSS tips for first/last child selectors but I am after something very much like Firefox bookmarks.

UPDATE:

I do appreciate all input so far, but I'll elaborate to avoid any further confusion;

  • I've written many plugins & themes, and have a good understanding of all the basics (hooking into WordPress, implementing extended functionality, handling & saving data etc.)
  • I'm specifically after advice on extending the nav menus admin UI, which would allow users to drop in 'separators' (nothing more than markers) between items in the menu, and how best to store that data & then render it in some shape or form on output with wp_nav_menu

Usually I would delve in & just get to work. But since this is only a possible requirement for an upcoming project, I thought I'd see if anyone's already tried something similar.

Plus I thought it might actually be useful for others, and would post my own feedback/results if I proceed with it.

3
  • #nav-menu li { border-right: 1px solid #fff; } #nav-menu li:last-child { border-right: none; }
    – Chris_O
    Jan 8, 2012 at 13:11
  • Did anyone read my question? I need to allow the end user to drop in separators at any point of their choosing. Jan 8, 2012 at 13:14
  • "allow a user to drop in separators/dividers between certain nav menu items" kinda implies end user. And I'm entirely aware of CSS/HTML requirements, this question is directed at suggested plugin techniques to build the required functionality. Jan 8, 2012 at 14:34

3 Answers 3

4

Use a custom walker. Extend start_el() to print <li class="menu_separator"><hr></li> if the menu title is just a '-'.

functions.php

function wpse38241_setup_menu()
{
    register_nav_menu( 'main-menu', 'Main Menu' );
}
add_action( 'after_setup_theme', 'wpse38241_setup_menu' );


/**
 * Replaces items with '-' as title with li class="menu_separator"
 *
 * @author Thomas Scholz (toscho)
 */
class Wpse38241_Separator_Walker extends Walker_Nav_Menu
{
    /**
     * Start the element output.
     *
     * @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. May be used for padding.
     * @param  array $args    Additional strings.
     * @return void
     */
    public function start_el( &$output, $item, $depth, $args )
    {
        if ( '-' === $item->title )
        {
            // you may remove the <hr> here and use plain CSS.
            $output .= '<li class="menu_separator"><hr>';
        }
        else
        {
            parent::start_el( &$output, $item, $depth, $args );
        }
    }
}

Create the menu

Now create your usual menu. Add an item with '-' as title:

enter image description here Full size image

Call the menu in your template

wp_nav_menu(
    array (
        'menu'            => 'main-menu',
        'container'       => FALSE,
        'container_id'    => FALSE,
        'menu_class'      => '',
        'menu_id'         => FALSE,
        'walker'          => new Wpse38241_Separator_Walker
    )
);

Output

(reformatted for readability)

<ul id="menu-separated" class="">
  <li id="menu-item-92" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-92">
    <a href="https://wordpress.stackexchange.com/questions/38241/nav-menu-separators">Nav menu separators?</a>
  </li>
  <li class="menu_separator">
    <hr>
  </li>
  <li id="menu-item-93" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-93">
    <a href="https://wordpress.stackexchange.com/">wordpress.stackexchange.com</a>
  </li>
</ul>

This works even without JavaScript and CSS.

2
  • Hmm liking that idea, quick and simple. Would still plan to bring in a new box on the left where the user could drag 'markers', which look & behave differently to standard menu items. Jan 8, 2012 at 14:55
  • 2
    Yes, absolutely. See this answer for custom meta boxes on nav-menu.php.
    – fuxia
    Jan 8, 2012 at 15:03
0

You're gonna have to have 2 tiers to this, a PHP script to store the user's preference and recreate it upon reentry and a JS script to allow them to place it. How you handle placing it is up to you, I'd probably go with a drag-and-drop interface. My script would go something like the following:

add_action( 'wp_enqueue_scripts', 'my_divider_enqueue' );
add_action( 'wp_ajax_insert-divider', 'ajax_insert_divider' );

// enqueue the script
function my_divider_enqueue() {
    // register the script elsewhere, preferably in an init hook
    wp_enqueue_script( 'divider-js' );
    $passed_vars = array(
        'ajaxurl' => admin_url('admin-ajax.php' ),
        'nonce'   => wp_create_nonce( 'divider-ajax-nonce' )
    );
    if( is_user_logged_in() )
        $passed_vars['prev'] => get_user_meta( $current_user->ID, 'divider_location', true ); // you shouldn't need to declare $current_user, but you may
    wp_localize_script( 'divider-js', 'divider', $passed_vars );
}
// handle the js
function ajax_insert_divider() {
    if( !wp_verify_nonce( $_POST['divider_nonce'], 'divider-ajax-nonce' ) ) {
        header("HTTP/1.0 401 Internal Server Error", true, 401);
        exit;
    } else {
        //may not be necessary, you can test this
        global $current_user;
        get_currentuserinfo();

        //update user meta
        update_user_meta( $current_user->ID, 'divider_location', $_POST['divider_location'] );
    }
    exit;
}

Docs: wp_register_script(), wp_enqueue_script(), wp_localize_script(), wp_create_nonce(), is_user_logged_in(), get_user_meta(), wp_verify_nonce(), get_currentuserinfo(), update_user_meta()

and then after that have some javascript/jQuery to handle when the user changes (this is pseudocode, since implementation is something you'll need to decide on)

jQuery(document).ready(function(){
    //when the user releases the divider in the desired location, do some stuff
    jQuery('.divider').mouseup(function() {
        if( divider_is_valid() ) {
            place_divider();
        } else {
            reset_divider();
        }
    });
    //using divider.prev, place the desired dividers
});
function reset_divider() {
    //put the divider back where it started, either in a holder for unused dividers or where it was in the menu previously
}
function place_divider() {
    //place divider where it's going
    jQuery.post(
        divider.ajaxurl,
        {
            action        : 'insert-divider',
            divider_nonce : divider.nonce,
            location      : //some representation of the location that you can call later
        },
        function( response ) {
            //execute any actions you need to do on response
        }
    );
}

Script is entirely untested (I wrote it directly into the answer editor lol), but it should get you going in the right direction.

4
  • Thanks for your input. However, I think I should be a little clearer with my requirement, question updated. Jan 8, 2012 at 14:50
  • Can you clarify a bit as to what specifically about this implementation is not to your liking, because as I read it it seems to meet all the requirements...clearly I'm reading something wrong lol
    – mor7ifer
    Jan 8, 2012 at 15:02
  • Firstly, I really do appreciate your time. But if I'm to be honest, all you've effectively shown me is how to implement an AJAX callback and save user meta data which a) doesn't really address my question at all and b) is something I've now made clear in my answer that I'm capable of (though I understand this was not at the time you provided your answer). Jan 8, 2012 at 15:25
  • Are you looking for something that will tie to the individual user and be customizable by that user, or something that will be site-wide?
    – mor7ifer
    Jan 8, 2012 at 16:05
-1

I'd use jQuery's .before() and .after() function, unless you are against it.

2
  • 3
    And how does this allow a user to place separators wherever they require them? Jan 8, 2012 at 13:15
  • By writing a plugin? Otherwise use custom menu and add empty links, which you then will style with CSS following your vision.
    – Asko
    Jan 8, 2012 at 13:23

Your Answer

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

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