prependChild on DOMElement in PHP, Sort of.

This evening I wanted to add a new LI onto a WordPress navigation programmatically. After considering a few different options, I decided I wanted to use PHP’s DOMDocument, and use this as an opportunity to more familiarize myself with its ins and outs.

Looking into DOMElement, I was pleased to see there was an appendChild() method, but a little down to see there there was no prependChild(). After searching online, I saw a few people creating custom classes to make their way around this absense, however I found a method that I prefer a little more.

Stumbling upon insertBefore(), I realized all of my problems had been solved…kinda.┬áinsertBefore() was leaving me just a bit confused; it sounds as though you call it from the element you wish to move around, but you don’t. In fact, you don’t even call it on the element you’re trying to snuggle up against. Instead, you call it on the parent of the two, referencing both elements as the arguments.

add_action( 'wp_nav_menu', 'custom_nav_menu' );
function custom_nav_menu( $menu ) {
  // Load it
  $html = new DOMDocument();
  $html->loadHTML( $menu );
  // Get it
  $list = $html->getElementsByTagName( 'ul' )->item( 0 );
  $first = $list->getElementsByTagName( 'li' )->item( 0 );
  $new_item = $html->createElement( 'li', 'Hello World' );
  // Place it
  $list->insertBefore( $new_item, $first );
  // Show it
  return $html->saveXML( $list->parentNode );

This gave the desired results, without requiring any new classes or other more complicated work-arounds. To my knowledge I don’t see any downfalls of it either, at least not at this time.

Some of you may be scratching your head at the last line, where I’m calling saveXML() rather than saveHTML(). As for 5.3.6 you can pass in an optional DOMNode to specify which portion of the DOMDocument you’d like to render. I happen to be using 5.3.5, so this option is not available to me and all I got to see was a nasty error message. Fortunately saveXML() worked as a nice substitute with little-to-no side-effects.