Removing the List Items for Cleaner WordPress Navigation Menus

The code in this mini-tutorial came from a Friday evening spent fiddling with a client project. I find that by Friday night I’m more apt to mess around with code for the sake of seeing what I can do, whereas the rest of the week that’s balanced a lot more with the need to get things done. Plus my husband works Friday nights and it gets pretty quiet once the kids are in bed.

The Problem

Last Friday, I was working on a custom theme for Brené Brown, a lovely client of mine (via Braid Creative who I can’t adequately describe without sounding like I’m bragging by association. We just moved her site from Squarespace to WordPress after doing the same with the site for her certification program, and I was working on putting the social media icons in the top navigation:

social-icons-in-nav

Nothing too crazy about that construct, I’ve done similar before.

The Strategy Decisions

There are a number of ways to handle it on both the content management side and the code side. Off the top of my head:

Content Management Options

  • Hard code the links and icons (bad news if the client ever changes an account)
  • Hard code most of it, but use custom fields/ theme options for the account URLs (I do this a lot)
  • Use a WordPress menu with classes on the links

I ended up going with third one, mainly because everything else in that area uses menus so that seemed like the most intuitive place to put it. Also, I’m not using theme settings or options for anything else, and didn’t want to add them just for this.

screen-options Just in case anyone is wondering, you can add classes (and link targets) to your menu items in WordPress by using the Screen Options tab on the top right of the admin area to enable the fields. You’ll then see them on your individual menu items:

link-entry-class-target

Useful, right?

Code Options

I went ahead and registered my new nav menu for the social media icons so that I could have them separate from the regular menus for styling purposes (and organization), and populated it, then put the regular navigation menu template tag into the theme header.php.

If this were a sitcom, you’d hear the squealing tires from slamming on the brakes right about here.

So, this theoretically works just fine. This is approximately the style of output that gets you:

<div class="menu-upper-menu-container">
    <ul id="menu-upper-menu" class="menu">
        <li id="menu-item-1" class="twitter menu-item current-menu-item">
            <a href="http://twitter.com/username" target="_blank">Blog</a>
        </li>
        <li><a><!-- you get the idea ---></a></li>
        <li><a><!-- ... ---></a></li>
    </ul>
</div>

Workable – you’d have to kind of beat the outer div and possibly the ul into submission since you likely want them to behave differently than the browser defaults and your other code dicatates, but it’s possible to do it that way, certainly.

For some reason, though, this didn’t appeal to me on Friday night – I wanted to get rid of the extra stuff, not have to override a bunch of stuff using specificity in my CSS, and just generally see if I could clean this up to work more in my favor and look more like how I’d hard-code it, in terms of HTML structure.

I had it in my head that would be great to get a list of links inside a single outer container as my final output.

The Solution

Per the Codex, there are existing parameters of wp_nav_menu(); that allow you to remove the outer container and swap out or remove the ul (container and items_wrap, respectively). The problem is, you’re still left with list items for each menu item. Not awesome.

My friend Google lead me to a snippet on CSS Tricks for removing the li elements using strip_tags, which kind of works but just gets rid of the list items entirely, including all the useful information on them like custom classes (and classes indicating the current page).

That concept made me think of the ever useful str_replace function, though, which ended up being just right for what I was looking to do. In case you aren’t familiar with it, the string replace PHP function does a find and replace in a string:

str_replace( $find, $replace, $searchstring);

Version 1: Semi-Clean

Here’s how I used this along with wp_nav_menu();

And here’s the general output structure:

<nav id="menu-upper-menu" class="menu">
    <span id="menu-item-1" class="twitter menu-item current-menu-item">
        <a href="http://twitter.com/username" target="_blank">Blog</a>
    </span>
    <span><a><!-- you get the idea ---></a></span>
    <span><a><!-- ... ---></a></v>
</nav>

It’s ok, I guess. The thing is, those span elements are totally extraneous, which was kind of grating on me.

Version 2: Really Clean

Then it occurred to me that you can use arrays to find and replace multiple strings at once within one str_replace function, where the first array is all “finds” and the second is all “replace withs” and they’re read in order (first find, first replace, then second find, second replace).

That lead to this modification:

And this output:

<nav id="menu-upper-menu" class="menu">
    <a id="menu-item-1" class="twitter menu-item current-menu-item" href="http://twitter.com/username" target="_blank">Blog</a>
    <a><!-- you get the idea ---></a>
    <a><!-- ... ---></a>
</nav>

Winning! We’ve maintained all our ids and classes, plus our link target, but gotten rid of all the excess for nice clean (HTML5!) menu code.

View both versions, plus all three output models on Github

  1. Excellent! Thanks for sharing this. Is there a reason why you decided against the unordered list instead of cleanliness’ sake? Unordered lists (in nav elements) are considered semantic, particularly good practice for screen readers. I know that kind of stuff is mostly all preference, just curious about your thoughts!

    • Zoe

      I’ve read a lot of articles about lists and nav menus (such as those on CSS Tricks and Sitepoint). I keep meaning to actually try it with a screen reader, as I don’t know that I’ve read anything I feel is definitive and I’d like to experience both ways myself.

      In this particular instance, it’s just a set of social media icons, not the main navigation (which I left as regular old lists), and mainly was a matter of avoiding super messy, not-really-future-proof code for handling those items. Certainly not meant to be a statement on semantics!

  2. Isn’t it faster and more convenient to use wp_get_nav_menu_items function and then build the menu of your dream?

    • Zoe

      I think it’s probably about equal in terms of time spent/ speed.

      In terms of convenience, I think the replacement method is probably easier for a newer user to understand quickly than the wp_get_nav_menu_items() function (codex reference, for anyone else who’s curious). Not that that makes it a better method, just a thought.

      I think it’s kind of a wash, really – either would work and probably equally well for this specific use case. There are definitely more complex use cases where wp_get_nav_menu_items() would come out on top, though!

  3. Mark

    Thanks for this, really quick and simple, and exactly what I was looking for.

    Just a quick pickup I noticed when using this, the following line is missing a semicolon in your example:
    $replace = array('','a')

  4. Robin

    Exactly what I was trying to accomplish, really clean output, bu couldn’t. Thank you!!

    • Zoe

      You’re welcome, glad it helped!

  5. Elise

    Hey this is great but I noticed that if your url in the link contains li for example the tradeelixir.com it will also replace that so it looks like this tradeeaxir.com

    Any ideas on how to avoid this?

    • Elise

      Ok so below is my work around on your code. Thanks for getting me started!

      [EDITED SINCE CODE IS IN A GIST BELOW]

    • Zoe

      Good find and thanks for sharing your updated code!

  6. Roman

    Today I’ve got the same problem to put social icons to the wordpress menu and finally come across your article. Thanks a lot, you saved my day!

Leave a Comment

Your email address will not be published. Required fields are marked *

About posting code: Please use a Gist or similar to share any extended code samples, and any code other than basic HTML, CSS, or jQuery/ JS, so as to keep the comments clean and readable. Basic HTML, CSS & jQuery can be shared in the comment surrounded by <code> tags. Thanks for commenting!