PHP Menu with jQuery Drop Down

I wanted to create a menu where each menu could have submenus. I wanted a way where I could keep adding more submenus very easily. Also when a you are on a page it would be selected and all the parent menus would still stay selected. I decided to make a function to recursively go through an array to create the menu.

I started out with an array() like this.

$tabs = array(“page.php”=>”Page Name”, “page2.php”=>”Page Two”,
“page3.php”=>”Page Three”, “page4″=>”Page Four”);

This is very simple to go through but lets say Page Two has submenus and those submenus have submenus. So I would set it up like this.

$subpage[‘Subpage 2’] = array(“subpg3.php”=>”Sub Sub Page”);
$page2[‘Page Two’] = array(“subpage.php”=>”Subpage Name”, “subpage2.php”=>$subpage);

$tabs = array(“page.php”=>”Page Name”, “page2.php”=>$page2,
“page3.php”=>”Page Three”, “page4″=>”Page Four”);

So now when you’re looping through all the $tabs you’ll need to loop through the $page2 array() as well and then the $subpage array and take the keys for each of the submenus to set the name for the parent menu. Also if I am on subpg3.php I still want to keep “Subpage 2” and “Page Two” selected.

Another thing is if we don’t want to show the page name in the menu but we still want the parent menus selected. All we do is set the value to “HIDDEN” like so:
$subpage[‘Page Two’] = array(“profile.php”=>”Profile”, “profile_edit.php”=>”HIDDEN”);

So here is the function to get all the tabs.

First I declare some variables.
The global $filename could simply be $_SERVER[“PHP_SELF”]. but I personally like to use preg_match to get only the filename since $_SERVER[‘PHP_SELF’] will return the filename along with any subfolder its in. So I do

preg_match("/.*\/(.*)/", $_SERVER['PHP_SELF'], $name);
$filename = $name[1];

The global $dropdown is used to store all the submenus. I used this with jquery to create dropdown menus. If you dont want dropdowns then you can disregard anything commented for dropdowns.

The $found gets set to 1 as soon as the $filename matches one of the tabs.
The $menu is the menu that gets returned. The menu gets returned in an array format like this:
$menu[0] = array(“Home”=>”<a href=’home.php’ class=’selected’>Home</a>”, “About”=>”<a href=’about.php’ >About</a>”);
$menu[2] = array(“test”=>”<a href=’test.php’ class=’selected’>Test</a>”);

you need to do a ksort on menu to make it go $menu[0], $menu[2], $menu[4] instead of $menu[0], $menu[4], $menu[2];


function recursive_tabs($tabs, $row=1){
    global $filename;
    global $dropdown;
    static $found=0;

    $row = $row-1;
    $menu= array();
    foreach($tabs as $key=>$tab){

I check to see if $tab is an array; if it is and $found == 0 then I go into this “if statement” which calls this function again.

     
        if(is_array($tab) && !$found){
            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);

            $tabkey = array_keys($tabs[$key]);

The next block is used for the dropdown array. We need to get all the keys of the $submenu. We need this to match up the $dropdown array correctly with its parent’s row. And then we need the keys of $tabs to match up the $dropdown with the name of its parent.
For example: if the $menu was
$menu[0] = array(“Home”=>”<a href=’home.php’ class=’selected’>Home</a>”, “About”=>”<a href=’about.php’ >About</a>”);

and $menu[0][‘Home’] had submenus then the dropdown would be $dropdown[0][‘Home’] = array(0=>”<a href=’drop.php’>Drop</a>”); so that they match up.


            $subkeys = array_keys($submenu);
            $sizeofkeys = array_keys($subkeys);

            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }

if the $row is not 0, 2, 4, etc.. then I want to go ahead and return the $submenu.


            if($row%2 != 0  && $row !=0){
                return $submenu;
            }

Next I need to set the row back to the previous number. Then if the filename matches the current tab I loop through the submenu and set it to the menu. Also I check to see if $tabkey[0] == ‘HIDDEN’. $tabkey[0] is the name of each file. If it equals HIDDEN I don’t store it in the menu array but the parents of it will still be selected.
If it is not found I simply add the current $key to the menu.


            $row = $row -2;
            if($filename==$key) $found = 1;
            if($found){
                foreach($submenu as $subkey=>$sub){
                   $menu[$subkey] = $sub;
                }
                if($tabkey[0] != "HIDDEN"){
                   $menu[$row][$tabkey[0]] = '<a class="selected"
                      href="'.$key.'">'.$tabkey[0].'</a>
                }
            }else{
                if($tabkey[0] != "HIDDEN")
                   $menu[$row][$tabkey[0]] = "<a
                        href='$key'>".$tabkey[0]."</a>";
            }

If $tab is an array but $found == 1 I go into this one.


        }else if(is_array($tab)){

            $tabkey = array_keys($tabs[$key]);
            if($tabkey[0] != "HIDDEN")
                $menu[$row][$tabkey[0]] = "<a href='$key'>
                      {$tabkey[0]}</a>";

This next block is for the dropdown menu. Same thing we used above.


            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);

            $subkeys = array_keys($submenu);

            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }
             $row = $row -2;

The last block is if $tab is not an array. If the $filename == $key then I set $found = 1 and also a change the class to “selected”.


        }else{
            if($filename==$key){
              if($tab != "HIDDEN"){
                $menu[$row][$tab] = '<a class="selected"
                                       href="'.$key.'" >'.$tab.'</a>
              }
              $found = 1;
            }
            else {
                if($tab != "HIDDEN")
                $menu[$row][$tab] = "<a href='$key'  >$tab</a>";
            }
        }
    }
     return $menu;
}

That’s it. Here is all the code together. If you want to see how to use this menu to create a drop down menu keep reading.


function recursive_tabs($tabs, $row=1){
    global $filename;
    global $dropdown;
    static $found=0;

    $row = $row-1;
    $menu= array();
    foreach($tabs as $key=>$tab){
        if(is_array($tab) && !$found){
            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);

            $tabkey = array_keys($tabs[$key]);
            $subkeys = array_keys($submenu);

            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }
            if($row%2 != 0  && $row !=0){
                return $submenu;
            }

            $row = $row -2;
            if($filename==$key) $found = 1;
            if($found){
                foreach($submenu as $subkey=>$sub){
                   $menu[$subkey] = $sub;
                }
                if($tabkey[0] != "HIDDEN"){
                $menu[$row][$tabkey[0]] = '<a class="selected"
                                      href="'.$key.'" >'.$tabkey[0].'</a>
                }
            }else{
                if($tabkey[0] != "HIDDEN")
                    $menu[$row][$tabkey[0]] = "<a
                                     href='$key'>".$tabkey[0]."</a>";
            }
        }else if(is_array($tab)){
            $tabkey = array_keys($tabs[$key]);
            if($tabkey[0] != "HIDDEN")
                $menu[$row][$tabkey[0]] = "<a href='$key' >
                                                          {$tabkey[0]}</a>";

            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);
            $subkeys = array_keys($submenu);
            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row && $row>2){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }
             $row = $row -2;
        }else{
            if($filename==$key){
              if($tab != "HIDDEN"){
                 $menu[$row][$tab] = '<a class="selected"
                                             href="'.$key.'" >'.$tab.'</a>
              }
              $found = 1;
            }
            else {
                if($tab != "HIDDEN")
                  $menu[$row][$tab] = "<a href='$key' >$tab</a>";
            }
        }
    }
     return $menu;
}

Once this function returns we’ll need to loop through all the menu’s.

This first thing I do is create an array of divs to wrap the menus in. This is used for styling.


$subdiv[0] = "<div id='toplinks_css'>";
$subdiv[1] = "<div id='sublinks_css'>";
$subdiv[2] = "<div id='sublinks2_css'>";

So as I’m looping through each of the menu items I add a wrapper div around each of them with a unique ID so it can be used with the jquery drop down. The drop down is also wrapped in this div. I also wrap a div around just the link. This is so jquery can can change the style of just that link if you wanted.
The next thing is if there is a drop down menu to add a wrapper around it with position of relative. This will allow each of the drop downs to be positioned under their parent menu. I then wrap the drop down in another div with a unique Id used by jquery and a class called dropdown. The main thing that you need to have in this class is position:absolute, display:none; and z-index:999 (“some number to make sure drop down goes on top”).


$head .= "<script type='text/javascript'>
$(document).ready(function(){";
$count = 0;
foreach($menu as $key=>$tabs){

$str .= $subdiv[$i];
foreach($tabs as $tkey=>$tab){
  $str .= "<div id='header$count' style='float:left; margin-left:20px;'>
       <div id='head$count'>$tab</div>";
  if($dropdown[$key][$tkey]){
     $str .= "<div style='position:relative;'>
                     <div id='dropdowncontainer$count' class='dropdown'>
                         <div class='inner'>";
     foreach($dropdown[$key][$tkey] as $dkey=>$dval){
       $str .= $dval;
    }
$str .= "</div>
</div>
</div><div style='clear:both;'></div>";
}

The next bit is the jQuery. I use a jQuery plugin called hoverIntent which allows you to set a time that the mouse must be over the link before it will execute. This way if you’re just moving the mouse across the page the drop down wont show up. It takes two functions. The first one is on hover and the second is off hover.


$head .= "$('#header$count').hoverIntent(function(){
    $('#head$count a').css('background-color','#0167B1');
";

   if($dropdown[$key][$tkey])
     $head .= "$('#dropdowncontainer$count').show();";
$head .= "    },

  function(){
    $('#head$count a').css('background-color','');";
    if($dropdown[$key][$tkey])
    $head .= "        $('#dropdowncontainer$count').hide();";

$head .= "  });  ";

$str .= "</div>";
$count++;
}
$str .= "<div style='clear:both;'></div></div>";
$i++;
}
$head .=   "});
</script>";

And that will give you a dropdown box which you can now style however you want.

Click here to view the example.