serraosays
serraosays

Reputation: 7869

Reverse menu items and anchor tags using a custom walker class in WordPress

WordPress navigation menus typically output as:

<ul>
  <li><a href="location.html">Menu Item 1</a></li>
  <li><a href="location2.html">Menu Item 2</a></li>
  <li><a href="location3.html">Menu Item 3</a></li>
</ul>

Typically you'd use a custom walker class in your theme to change around the arguments feed into these elements but how do you change the order they load in? This is my goal:

<ul>
  <a href="location.html"><li>Menu Item 1</li></a>
  <a href="location2.html"><li>Menu Item 2</li></a>
  <a href="location3.html"><li>Menu Item 3</li></a>
</ul>

Upvotes: 0

Views: 291

Answers (2)

Alex C
Alex C

Reputation: 951

There is also another more simple sollution to this. I faced the same problem with Wordpress and a bootstrap menu which had right alignment in css.

What I did was capture the output from wp_nav_menu by setting 'echo' to false:

$output = wp_nav_menu(array('echo' => false));

Once you have the output the possibilities are endless, for instance you could use:

explode($delimiter, $output);

to split the string into tokens, reverse them and so on. Make sure that you prepend the delimiter back again or use regex if you go down this path.

For details on wp_nav_menu see: wp_nav_menu documentation

'echo' (bool) Whether to echo the menu or return it. Default true.

Upvotes: 1

serraosays
serraosays

Reputation: 7869

After hours of research, I figured out that this is not possible with a custom walker class. WordPress specifically mentions in the comments of class-walker-nav-menu.php that the <li> themselves cannot be modified, lines 189-203:

       /**
         * Filters a menu item's starting output.
         *
         * The menu item's starting output only includes `$args->before`, the opening `<a>`,
         * the menu item's title, the closing `</a>`, and `$args->after`. Currently, there is
         * no filter for modifying the opening and closing `<li>` for a menu item.
         *
         * @since 3.0.0
         *
         * @param string $item_output The menu item's starting HTML output.
         * @param object $item        Menu item data object.
         * @param int    $depth       Depth of menu item. Used for padding.
         * @param array  $args        An array of wp_nav_menu() arguments.
         */
        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

Instead, I had to do this with Javascript (obviously not ideal but doable). This is far trickier than it first appears. You have to setup a regex that will read through your wp_nav_class output and then move around the pieces through multidimensional arrays. Read through the code below:

// 1- Get entire content of sidebar menu ul, then split by row
var listItems = $(".sidebar-nav").html();
var listItemsArray = listItems.match(/(.*<\/li>)/gm);

// 1b- Set up variables
var listItemsTemp = [];
var listItemsRearranged = new Array(listItemsArray.length);
var listItemsRearrangedConcat = new Array(listItemsArray.length);

// 2- Take listItemsArray and loop through it w/ regex
for (var i = 0; i < listItemsArray.length; i++) {

  // 4- Use regex to break each list item into five pieces
  listItemsTemp[i] = listItemsArray[i].match(/((<\w+\b>)|(<a\shref=".*">)|(\w+\b)|(<\/\w+\b)>)/g);

  // 5- Setup listItemsRearranged[i] with an array of listItemsTemp.length
  listItemsRearranged[i] = new Array(listItemsTemp.length);

  // 6- Take listItemsTemp and reassign positions
  // We can do this because our string will always have five pieces
  listItemsRearranged[i][0] = listItemsTemp[i][1];
  listItemsRearranged[i][1] = listItemsTemp[i][0];
  listItemsRearranged[i][2] = listItemsTemp[i][2];
  listItemsRearranged[i][3] = listItemsTemp[i][4];
  listItemsRearranged[i][4] = listItemsTemp[i][3];

  // 7- Join the array back into a string
  listItemsRearrangedConcat[i] = listItemsRearranged[i].join("");
}

// 8- Finally, we rejoin the string and print back into the page
var listItemsFinal = listItemsRearrangedConcat.join("");
$(".sidebar-nav").html(listItemsFinal);

Upvotes: 0

Related Questions