Create a dynamic menu / navigation bar with PHP

Lets create some PHP-code which helps creating a navigation bar that highlights the current choice. Lets use an array to define the menu and a class with one static method that generates the HTML code from the array entries.

Lets start with the array

A menu consist of choices where each choice has a text and a link. Lets represent this in an array. It could look like this:

<?php
$menu 
= array(
  
'home'  => array('text'=>'Home',  'url'=>'?p=home'),
  
'away'  => array('text'=>'Away',  'url'=>'?p=away'),
  
'about' => array('text'=>'About''url'=>'?p=about'),
);

Nice, a simple two-dimensional array to start with.

Create a class and method to generate HTML for the menu

A class, dedicated to create menus, is a nice way to structure your code. It could look like this:

<?php
class CNavigation {
  public static function 
GenerateMenu($items) {
    
$html "<nav>\n";
    foreach(
$items as $item) {
      
$html .= "<a href='{$item['url']}'>{$item['text']}</a>\n";
    }
    
$html .= "</nav>\n";
    return 
$html;
  }
};

We make the method static since there is no need to access $this from this method.

If you dont want it as a class? So skip it, just make a function for it. It could look like this:

<?php
function generateMenu($items) {
  
$html "<nav>\n";
  foreach(
$items as $item) {
    
$html .= "<a href='{$item['url']}'>{$item['text']}</a>\n";
  }
  
$html .= "</nav>\n";
  return 
$html;
}

I'll move on with tha class approach, I feel for classes today.

First try! How does it look?

So, we call the method and send the array as the argument. Like this.

<?php echo CNavigation::GenerateMenu($menu);

And the result displays like this:

The HTML for the generated menu looks like this:

<nav class='navbar'>
<a href='?p=home#2'>Home</a>
<a href='?p=away#2'>Away</a>
<a href='?p=about#2'>About</a>
</nav>

Ok, its nice but adding some style would make it much nicer. Watch out for the hashmark #, its only there so you can click on the menu in this tutorial and end up in the same part of this page, its purely usability for the tutorial.

Styling the menu

There is a category with examples in Style! that shows various ways of styling such a menu. Lets look at those and choose some style for this example. I'll go with the following style:

.navbar {display block;background-color:#333;font-family:Verdana;padding:0.5em;}
.navbar a {background-color:#999;color:#fff;padding:0.2em;text-decoration:none;}
.navbar a:hover {background-color:#666;padding:0.5em 0.2em 0.5em 0.2em}

I gathered all style in the class navbar so it must be put together with the nav element. To make this happen we add a extra argument, $class that can be sent to the generate-method, now looking like this:
GenerateMenu($items, $class).

<?php
class CNavigation {
  public static function 
GenerateMenu($items$class) {
    
$html "<nav class='$class'>\n";
    foreach(
$items as $item) {
      
$html .= "<a href='{$item['url']}'>{$item['text']}</a>\n";
    }
    
$html .= "</nav>\n";
  }
};

Trying it out provides this result:

The HTML for the generated menu looks like this:

<nav class='navbar'>
<a href='?p=home#2'>Home</a>
<a href='?p=away#2'>Away</a>
<a href='?p=about#2'>About</a>
</nav>

Highlighting the current choice

To be able to highlight the current choice we need an extra class in the stylesheet and we need to figure out which menu choice is the current one by using PHP. How the PHP-logic is done depends on how the url looks like. There are various was of doing this and let us see two ways, one simple and one more advanced.

We use the following style for the active choice, this means that we have to add the class selected to the a-element of the navbar to make it have the current-look.

.navbar a.selected {background-color:#fff;padding:0.3em 0.2em 0.5em 0.2em;color:#333;}

The simple solution

We know that the url looks like this ?p=home and we know that the value of p is the same as the key in the array:

  'home'  => array('text'=>'Home',  'url'=>'?p=home'),

Knowing this we can add an if-statement to the generate-method that checks if the array-key is the same as the $_GET['p'] variable and if it is true then add the class selected for that item. It can look like this:

<?php
class CNavigation {
  public static function 
GenerateMenu($items$class) {
    
$html "<nav class='$class'>\n";
    foreach(
$items as $key => $item) {
      
$selected = (isset($_GET['p'])) && $_GET['p'] == $key 'selected' null
      
$html .= "<a href='{$item['url']}' class='{$selected}'>{$item['text']}</a>\n";
    }
    
$html .= "</nav>\n";
    return 
$html;
  }
};

Trying it out provides this result, test it by clicking the menu and study the url of this page.

The HTML for the generated menu looks like this:

<nav class='navbar'>
<a href='?p=home#3' class=''>Home</a>
<a href='?p=away#3' class=''>Away</a>
<a href='?p=about#3' class=''>About</a>
</nav>

The drawbacks of this simple method is that the CNavigation class needs to know about the structure of the urls of the site and that it only supports on url-structure. But, the advantage is that its quick and can be rewritten in no time.

A more advanced solution

We add a callback for each menu, and store it in the menu-array. The idea is that the generate-method can call this callback before rendering the HTML. In this callback, which know the special settings of this particular menu and knows about the url structure of the site, then we change the menu array and add a class attribute to the selected item. Lets see how it can be done.

First we create the callback function and add it to the menu array, we also rearrange the menu-array and store all the items in their own array.

<?php
function modifyNavbar($items) {
  
$ref = isset($_GET['p']) && isset($items[$_GET['p']]) ? $_GET['p'] : null;
  if(
$ref) {
    
$items[$ref]['class'] .= 'selected'
  }
  return 
$items;
}

$menu = array(
  
'callback' => 'modifyNavbar',
  
'items' => array(
    
'home'  => array('text'=>'Home',  'url'=>'?p=home''class'=>null),
    
'away'  => array('text'=>'Away',  'url'=>'?p=away''class'=>null),
    
'about' => array('text'=>'About''url'=>'?p=about''class'=>null),
  ),
);

Now we have all the logic, for choosing the selected item, captured within the menu array. This means that each menu can have its own logic for how the current choice is seleced. We update the CNavigation::GenerateMenu to call the callback and make use of the class attribute of the menu array. It now looks like this:

<?php
  
public static function GenerateMenu($menu$class) {
    if(isset(
$menu['callback'])) {
      
$items call_user_func($menu['callback'], $menu['items']);
    }
    
$html "<nav class='$class'>\n";
    foreach(
$items as $item) {
      
$html .= "<a href='{$item['url']}' class='{$item['class']}'>{$item['text']}</a>\n";
    }
    
$html .= "</nav>\n";
    return 
$html;
  }

Trying it out provides this result, test it by clicking the menu and study the url of this page.

The HTML for the generated menu looks like this (same as previous example):

<nav class='navbar'>
<a href='?p=home#4' class=''>Home</a>
<a href='?p=away#4' class=''>Away</a>
<a href='?p=about#4' class=''>About</a>
</nav>

Done

This was a way to get dynamic menu handling using HTML, CSS and PHP. This technique can make it easier to manage menus and keep track of selected choices when developing larger sites and making use of several menus.

If you had difficulties understanding the last part, then just stick with the simpler path, it should be good enough for most cases and you can modify it for your purpose.

Ask about and discuss this tutorial in the forum.

/Mikael