Split navigation is a popular, albeit difficult to implement gracefully, solution for many designers when trying to make a standard navigation edgy. It’s not as easy to manage from a CMS/CSS standpoint as a standard menu, especially when you need to create a universal hierarchy (which you should be doing if you’re delivering to a client). I ran into this and have come up with a good solution that works in 99% of situations where we need to take the nav, split it, and have the parent page as a centerpiece. Take a look at the mockup below:

mockup

 

We have two problems here: there is a split in the menu at the top, and there is a split in the menu below equating to a few different solutions. The first solution is the most obvious: use multiple menus. This isn’t very user friendly for whoever is going to be managing this. So, if we have a menu hierarchy like this:

  • Menu 1
    • Submenu 1
    • Submenu 2
    • Submenu 3
    • Submenu 4
  • Menu 2
  • Menu 3
  • Menu 4
  • Menu 5
  • Menu 6

Then we’re going to have to break that menu appropriately and detect which part of the menu is active, etc. Let’s start with the header nav, that’s the easiest:

Header Nav

mockup_jpg___113___RGB_8____

Based on the breakdown above, we’re going to be splitting this into three pieces: the left half of the nav, the logo, and the right half of the nav. To do this, we need to get the menu object we’re wanting to use, get the menu items that don’t have parents (the top level menu items) and then array_split it:

//Get our menu object
$menu_name = 'main-nav';
     //Check to see if our menu object exists and is set
     if(($locations = get_nav_menu_locations()) && isset($locations[$menu_name])){
          $menu = wp_get_nav_menu_object($locations[$menu_name]);
          $menu_items = wp_get_nav_menu_items($menu->term_id);

          //Create a new array with just the top level objects
          $newMenu = array();
          foreach($menu_items as $item){
               if($item->menu_item_parent != 0) continue;
               array_push($newMenu, $item);
          }

          //Split menu array in half
          $len = count($newMenu);
          $firsthalf = array_slice($newMenu, 0, $len / 2);
          $secondhalf = array_slice($newMenu, $len / 2);
}

So above we are checking to see if our menu object exists, for this example I’ve named my menu main-nav when I registered it in functions.php. If it does then we get the nav items (wp_get_nav_items) as an array. Since the array is an array of objects we can’t just filter the array based on a key, we’re going to have to crawl through it and create a new array with just the objects we want, in this case we are crawling through and skipping any objects that have a key menu_item_parent set higher than 0. This leaves us with a new array ($newMenu) that only has nav objects that are at the top of the menu hierarchy. After that we take $newMenu, get it’s count() and then split is using array_slice by the count divided by 2 ($len/2). This leaves us with two new arrays, each ready to be crawled:

//Get our menu object
$menu_name = 'main-nav';
     //Check to see if our menu object exists and is set
     if(($locations = get_nav_menu_locations()) && isset($locations[$menu_name])){
          $menu = wp_get_nav_menu_object($locations[$menu_name]);
          $menu_items = wp_get_nav_menu_items($menu->term_id);

          //Create a new array with just the top level objects
          $newMenu = array();
          foreach($menu_items as $item){
               if($item->menu_item_parent != 0) continue;
               array_push($newMenu, $item);
          }

          //Split menu array in half
          $len = count($newMenu);
          $firsthalf = array_slice($newMenu, 0, $len / 2);
          $secondhalf = array_slice($newMenu, $len / 2);

          //Create left menu
          echo '<div id="headerMenuLeft"><ul>';
          foreach($firsthalf as $item){
               echo "<li><a href='".$item->url."'>".$item->title."</a></li>";
          }
          echo '</ul></div>'; ?>

          <div id="logo">
               <a href="<?php echo site_url(); ?>"><img src="<?php header_image(); ?>"></a>
          </div>

          <?php //Create right menu
          echo '<div id="headerMenuRight"><ul>';
          foreach($secondhalf as $item){
               echo "<li><a href='".$item->url."'>".$item->title."</a></li>";
          }
          echo '</ul></div>';
}

The orange text above is where we are rendering our menus based on the contents of the object keys. Each new array that we created earlier holds a nav object with the following keys:

  • ID
  • post_author
  • post_date
  • post_date_gmt
  • post_content
  • post_title
  • post_excerpt
  • post_status
  • comment_status
  • ping_status
  • post_password
  • post_name
  • to_ping
  • pinged
  • post_modified
  • post_modified_gmt
  • post_content_filtered
  • post_parent
  • guid
  • menu_order
  • post_type
  • post_mime_type
  • comment_count
  • filter
  • db_id
  • menu_item_parent
  • object_id
  • object
  • type
  • type_label
  • url
  • title
  • target
  • attr_title
  • description
  • classes
  • xfn

For the sake of the mockup, we only need the $item->title and the $item->url, but the object contains all sorts of data about the post that it’s associated with as well as the actual nav item. You could make some pretty complicated menu walkers with that, but this demo is a little more simple. With that snippet above we are greeted with three divs: #headerMenuLeft, #logo, and #headerMenuRight containing the left menu array, the header_image, and the right menu array respectively. Now, getting into the subnav is little more complicated, but it’s the same concept as what’s above.

Subnav

Untitled-1___113___submenu_3__submenu_4__RGB_8___

Just like before, the mockup will be split into three portions, but this time around the center portion is going to be dynamic, so we’ll have to take that into account:

//Get our menu object
$menu_name = 'main-nav';
     if(($locations = get_nav_menu_locations()) && isset($locations[$menu_name])){
          $menu = wp_get_nav_menu_object($locations[$menu_name]);
          $menu_items = wp_get_nav_menu_items($menu->term_id);
          //Get current menu item
          $item = null;
          $v = $post->ID;
          foreach($menu_items as $obj) {
               if ($v == $obj->object_id) {
                    $item = $obj;
                    break;
                    }
               }
               //Is $item a child?
               if($item->menu_item_parent != 0){
                   $p = $item->menu_item_parent;
                   $item = null;
                   $v = $p;
                   foreach($menu_items as $obj) {
                       if ($v == $obj->ID) {
                           $item = $obj;
                           break;
                       }
                   }
                   $parent = $item->object_id;
               }else{
                   $parent = $item->object_id;
               }
               //Create a new array with just the post objects
               $newMenu = array();
               foreach($menu_items as $item){
                   $parentID = get_post_meta( $item->menu_item_parent, '_menu_item_object_id', true );
                   if($parentID != $parent) continue;
                   array_push($newMenu, $item);
               }
               //Split menu array in half
               $len = count($newMenu);
               $firsthalf = array_slice($newMenu, 0, $len / 2);
               $secondhalf = array_slice($newMenu, $len / 2);
     }

From here it’s largely the same except for this part:

//Get current menu item
$item = null;
$v = $post->ID;
foreach($menu_items as $obj) {
     if ($v == $obj->object_id) {
          $item = $obj;
          break;
          }
     }
     //Is $item a child?
     if($item->menu_item_parent != 0){
          $p = $item->menu_item_parent;
          $item = null;
          $v = $p;
          foreach($menu_items as $obj) {
               if ($v == $obj->ID) {
                    $item = $obj;
                    break;
               }
          }
               $parent = $item->object_id;
          }else{
               $parent = $item->object_id;
          }

This time around we need to detect the current page’s ID in order to determine where in the menu to crawl. To do this we’re going to crawl the $menu_items array until we find an object where the object_id key matches our post ID. Once we have that we can then determine whether this post is a child page or not by checking if our newly found object has menu_item_parent set above 0, just like before. If it doesn’t, then it’s the parent of the current submenu, otherwise we need to recrawl the $menu_items array to be get our item’s parent’s object_id. With that in hand we can render our menus the same as last time, but for the centerpiece instead of the logo we’re going to setup the postdata for that object_id we got earlier:

//Render the menus
echo '<div id="subnavLeft"><ul>';
foreach($firsthalf as $item){
     echo "<li><a href='".$item->url."'>".$item->title."</a></li>";
}
echo '</ul> </div>';

//Render our parent menu item/center piece
$post = get_post($parent);
echo "<div id='subnavCurrent'>
     <a href='".$post->guid."'>".$post->post_title."</a>
</div>";

//Create right menu
echo '<div id="subnavRight"><ul>';
foreach($secondhalf as $item){
     echo "<li><a href='".$item->url."'>".$item->title."</a></li>";
}
echo '</ul></div>';

So above we render the left menu based on that split left array, then we get the post based on the $parent variable set when determining if the current page is the parent or not, then we render the right menu. It’s a simple concept but requires some menu crawling that can be complicated if you’re not as versed in PHP or how WordPress aliases it’s objects.

Download it!

You can download the snippets below to use it yourself in your projects. If this has been helpful please leave some kind words below, and give some attribution. Everything on here is free to use, but I like to hear that what I’ve done is helpful in some way.

 

Download – Split Menu | Snippet