Building an Extensible Menu Class - Reaching Higher (
Page 8 of 10 )
Now, while that's all fine
and dandy, a portal is perhaps the simplest application of this class. It
remains to be seen if it can be used with other, more complex menu interfaces.
So let's put it to the test, by putting it in the ring with some popular
JavaScript-based menu systems.
The first of these is the very popular
HIERmenus script (available at http://www.webreference.com/dhtml/hiermenus/ ).
This very flexible menu system is completely written in JavaScript, and relies
on JavaScript arrays (packaged in a specific format) to build a hierarchical
menu tree. I'm not going to get into the nitty-gritty of how it works - there's
some excellent documentation if you're interested - but rather plan to focus on
how this client-side code can be connected to a database of menu items via the
Menu class.
Let's suppose that I wanted to build a menu tree which looked
like this:
mysql> SELECT id, label, parent FROM menu;
+----+------------------------+--------+
| id | label | parent |
+----+------------------------+--------+
| 1 | Services | 0 |
| 2 | Company | 0 |
| 3 | Media Center | 0 |
| 4 | Your Account | 0 |
| 5 | Community | 0 |
| 6 | For Content Publishers | 1 |
| 7 | For Small Businesses | 1 |
| 8 | Background | 2 |
| 9 | Clients | 2 |
| 10 | Addresses | 2 |
| 11 | Jobs | 2 |
| 12 | News | 2 |
| 13 | Press Releases | 3 |
| 14 | Media Kit | 3 |
| 15 | Log In | 4 |
| 16 | Columns | 5 |
| 17 | Colophon | 16 |
| 18 | Cut | 16 |
| 19 | Boombox | 16 |
| 20 | The HITG Report | 16 |
| 21 | Trog | 16 |
+----+------------------------+--------+
21 rows in set (0.06 sec)
If I was to build this menu using the HIERmenus system, I
would need to manually code a series of JavaScript arrays, like this:
HM_Array1 = [ [180, 200, 50, "black", "white", "white", "black", "black",
"gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Services",
"http://www.melonfire.com/services/", 1, 0, 1], ["Company",
"http://www.melonfire.com/company/", 1, 0, 1], ["Media Center",
"http://www.melonfire.com/mcenter/", 1, 0, 1], ["Your Account",
"http://www.melonfire.com/account/", 1, 0, 1], ["Community",
"http://www.melonfire.com/community/", 1, 0, 1] ];
HM_Array1_1 = [ [180, 200, 50, "black", "white", "white", "black", "black",
"gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["For Content Publishers",
"http://www.melonfire.com/services/content.html", 1, 0, 0], ["For Small
Businesses", "http://www.melonfire.com/services/sbs.html", 1, 0, 0] ];
HM_Array1_2 = [ [180, 200, 50, "black", "white", "white", "black", "black",
"gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Background",
"http://www.melonfire.com/company/background.html", 1, 0, 0], ["Clients",
"http://www.melonfire.com/company/clients.html", 1, 0, 0], ["Addresses",
"http://www.melonfire.com/company/addresses.html", 1, 0, 0], ["Jobs",
"http://www.melonfire.com/company/jobs.html", 1, 0, 0], ["News",
"http://www.melonfire.com/company/news.php3", 1, 0, 0] ];
HM_Array1_3 = [ [180, 200, 50, "black", "white", "white", "black", "black",
"gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Press Releases",
"http://www.melonfire.com/mcenter/pr.html", 1, 0, 0], ["Media Kit",
"http://www.melonfire.com/mcenter/mkit.html", 1, 0, 0] ];
HM_Array1_4 = [ [180, 200, 50, "black", "white", "white", "black", "black",
"gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Log In",
"http://www.melonfire.com/account/index.html", 1, 0, 0] ];
HM_Array1_5 = [ [180, 200, 50, "black", "white", "white", "black", "black",
"gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Columns",
"http://www.melonfire.com/community/columns/", 1, 0, 1] ];
HM_Array1_5_1 = [ [180, 200, 50, "black", "white", "white", "black",
"black", "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Colophon",
"http://www.melonfire.com/community/columns/colophon/", 1, 0, 0], ["Cut",
"http://www.melonfire.com/community/columns/cut/", 1, 0, 0], ["Boombox",
"http://www.melonfire.com/community/columns/boombox/", 1, 0, 0], ["The HITG
Report", "http://www.melonfire.com/community/columns/thr/", 1, 0, 0],
["Trog", "http://www.melonfire.com/community/columns/trog/", 1, 0, 0] ];
The downside here is obvious - each time I want to change the
menu structure, I have to go into the JavaScript source and muck about with the
arrays (which aren't exactly all that user-friendly to begin with.) What I would
prefer to do, however, is somehow interface my database table to the HIERmenus
system, so that updating the menu becomes as simple as executing an SQL query to
change the relationships in the mySQL table.
With the help of some clever
PHP code and the Menu object I've just built, this becomes not just possible,
but a snap to accomplish. The first step is to alter the HIERmenus scripts to
reference a PHP file for the arrays, rather than a JavaScript file - simply
alter the reference to "HM_Arrays.js" in the file "HM_Loader.js" to
"HM_Arrays.js.php", as below.
if(HM_IsMenu) {
document.write("<SCR" + "IPT LANGUAGE='JavaScript1.2'
SRC='HM_Arrays.js.php ' TYPE='text/javascript'><\/SCR" + "IPT>");
document.write("<SCR" + "IPT LANGUAGE='JavaScript1.2' SRC='HM_Script"+
HM_BrowserString +".js' TYPE='text/javascript'><\/SCR" + "IPT>");
}
Next, create the script "HM_Arrays.js.php", and populate it
with the following PHP code:
<?
// options to configure menu appearance
$HM_Menu_Options = "150, 200, 50, \"black\", \"white\", \"white\",
\"black\", \"black\", \"gray\", 0, 0, 0, 1, 1, 1, \"null\", \"null\", ,";
// create object
include("menu.class.php");
$obj = new Menu;
// run recursive function
buildMenu(0, "HM_Array1");
// this is a recursive function to generate
// the JS arrays needed by HIERmenus
// this function accepts an id (used to generate children)
// and a prefix string (attached to every array name, needed by HIERmenus)
// for more information on how this works and syntax,
// refer to HIERmenus documentation
function buildMenu($id, $prefix)
{
// obtain a reference to the Menu object
// and also source the options
global $obj;
global $HM_Menu_Options;
// get children of this node
$children = $obj->get_children($id);
// create a JS array with the correct name
$array_str = $prefix . " = [ [" . $HM_Menu_Options . "]";
// iterate through child nodes and create individual array elements
for ($x=0; $x<sizeof($children); $x++)
{
$array_elements_str = ", [\"" . $children[$x]['label'] . "\", \"" .
$children[$x]['link'] . "\", 1, 0, " . $obj->get_type($children[$x]['id'])
. "]";
// if a child node has further children,
// recurse with appropriate modification to the prefix
if($obj->get_type($children[$x]['id']) == 1)
{
$temp = $prefix . "_" . ($x+1);
buildMenu($children[$x]['id'], $temp);
}
// add the final list of array elements to the main array
$array_str .= $array_elements_str;
}
// close the JS array
$array_str .= " ];";
// and print it
print $array_str;
}
?>
The end result of all this processing: a set of JavaScript
arrays containing the various menu nodes, in a format which is acceptable to
HIERmenus. This is accomplished by means of a recursive function (conceptually
identical to the one used in the print_menu_tree() method) which takes care of
iterating through the various levels of the menu tree and printing arrays in the
format required by HIERmenus.
Now all we need is a HTML page which
activates the HIERmenus system and displays the menu tree in all its glory.
<html>
<head>
<basefont face="Arial">
<script language="JavaScript" type="text/javascript">
<!--
if(window.event + "" == "undefined") event = null;
function HM_f_PopUp(){return false};
function HM_f_PopDown(){return false};
popUp = HM_f_PopUp;
popDown = HM_f_PopDown;
//-->
</script>
</head>
<body>
<a href="#" onMouseOver="popUp('HM_Menu1',event)"
onMouseOut="popDown('HM_Menu1')">Roll your mouse over this link to see a
menu</a>
<script language="JavaScript1.2" src="HM_Loader.js"
type='text/javascript'></script>
</body>
</html>
And here's what it looks like:

Don't worry too much about the JavaScript code in this
example - it is merely standard code required for HIERmenus to work, and is
clearly documented by the developers of the system. The important thing to note
here is that we've taken a pure client-side application and successfully
connected it to a server-side database using standard method calls within the
Menu object.
Of course, this solution may not be ideal in every case.
Using a database and a PHP script to generate the JavaScript arrays dynamically
(rather than storing and using arrays from static JavaScript files) may degrade
performance; however, it does offer a benefit from the point of view of simpler
maintenance of menu tree relationships. It's much easier to alter relationships
in a mySQL table than it is to open up a JavaScript file and edit the
information in it; a non-technical person can easily accomplish the former, but
may have difficulty with the latter. Consequently, a thorough cost-benefit
analysis should be performed before making a decision as to which option is best
suited to a specific case.