Build a simple infinite depth category system for your site, subcategories expand using javascript without refreshing the page.
Let's start by showing the database structure and data
CREATE TABLE `categories` ( `id` int(11) NOT NULL auto_increment, `name` varchar(100) NOT NULL, `parent` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=13 ; -- -- Dumping data for table `categories` -- INSERT INTO `categories` (`id`, `name`, `parent`) VALUES (1, 'Web development', 0), (2, 'Application development', 0), (3, 'Linux', 0), (4, 'Misc', 0), (5, 'Php', 1), (6, 'Mysql', 1), (7, 'Javascript', 1), (8, 'CSS', 1), (9, 'C plus plus', 2), (10, 'wxWidgets', 2), (11, 'Tutorials', 3), (12, 'My thoughts', 4);
We are using a single table to hold together categories and subcategories. Each subcategory can have a parent to show which category it belongs to, and they can be linked recursively
In this example, we have only 1 level subcategories, but you can add how many levels you would like to. For example category 'Web development' has 4 subcategories: Php, Mysql, Javascript and CSS
Using data from this table we will generate a nested html unordered list using the php code below
<?php
//connect to database
$link = mysqli_connect('localhost','root','');
mysqli_select_db($link,'your_database_name');
//get all rows
$query = mysqli_query($link,'SELECT * FROM categories');
while ( $row = mysqli_fetch_assoc($query) )
{
$menu_array[$row['id']] = array('name' => $row['name'],'parent' => $row['parent']);
}
//recursive function that prints categories as a nested html unorderd list
function generate_menu($parent)
{
$has_childs = false;
//this prevents printing 'ul' if we don't have subcategories for this category
global $menu_array;
//use global array variable instead of a local variable to lower stack memory requierment
foreach($menu_array as $key => $value)
{
if ($value['parent'] == $parent)
{
//if this is the first child print '<ul>'
if ($has_childs === false)
{
//don't print '<ul>' multiple times
$has_childs = true;
echo '<ul>';
}
echo '<li><a href="/category/' . $value['name'] . '/">' . $value['name'] . '</a>';
generate_menu($key);
//call function again to generate nested list for subcategories belonging to this category
echo '</li>';
}
}
if ($has_childs === true) echo '</ul>';
}
//generate menu starting with parent categories (that have a 0 parent)
generate_menu(0);
And the resulting html unordered list looks like this
<ul id="categories"> <li>Web development <ul> <li><a href="/category/Php/">Php</a></li> <li><a href="/category/Mysql/">Mysql</a> </li><li><a href="/category/Javascript/">Javascript</a></li> <li><a href="/category/CSS/">CSS</a></li> </ul> </li> <li>Application development <ul> <li><a href="/category/C-plus-plus/">C plus plus</a></li ><li><a href="/category/wxWidgets/">wxWidgets</a></li> </ul> </li> <li>Linux <ul> <li><a href="/category/Tutorials/">Tutorials</a></li> </ul> </li> <li>Misc <ul> <li><a href="/category/My-thoughts/">My thoughts</a></li> </ul> </li> </ul>
Now there are to ways of displaying your category menu. One is by using only css like this
#categories
{
list-style-position:inside;
list-style-image: url(/examples/categories/bullet.png);
font-size:10px;
font-family:Tahoma,Verdana,Arial;
margin:0px;
padding:0px;
}
And the other is by using javascript to make your categories expandable. This is useful if you have lots of subcategories
<ul id="categories"> <li>Web development <ul> <li><a href="/category/Php/">Php</a></li> <li><a href="/category/Mysql/">Mysql</a> </li><li><a href="/category/Javascript/">Javascript</a></li> <li><a href="/category/CSS/">CSS</a></li> </ul> </li> <li>Application development <ul> <li><a href="/category/C-plus-plus/">C plus plus</a></li ><li><a href="/category/wxWidgets/">wxWidgets</a></li> </ul> </li> <li>Linux <ul> <li><a href="/category/Tutorials/">Tutorials</a></li> </ul> </li> <li>Misc <ul> <li><a href="/category/My-thoughts/">My thoughts</a></li> </ul> </li> </ul> <script>menu_initiate();</script>
Download all files for this example, including javascript from here
Icons from sweetie.sublink.ca, javascript code (slightly improved) from javascript.internet.com
Update:Rik Moncur sent me a menu implementation based on this code, you can download the code from here dynamicmenu.zip, thanks Rik
Share this with the world
Related
Comments
The css version doesn't seem to function as per your example i.e. the first item in the menu has href link but should be just text?
Posted on 2007-10-05 16:02:58Hope that makes sense - great tutorial though :-)
Regards
Jason
Yes, your observation is correct, I removed the links from top categories, because I don't have those categories on my website, and I don't like putting # on my links.
Posted on 2007-11-19 08:53:44Thanks.
Thank you very much for your article. It helped me a lot!
Posted on 2007-11-19 08:52:58Hi,
Posted on 2007-11-09 08:39:34I have been looking for something like this for ages, thank you so much! I just have one problem, and I'm sure it would just require a little tweaking of some sorts...When the initial parent level (0) is generated, the only 0 level links that is displayed is 'Web Development' with 'PHP" underneath and that's it! Do you know why this is happening, or even better how I can fix it?
Many Thanks in advance Schalk
Hi
Posted on 2007-11-26 07:32:42Having the same problem as Schalk above - works perfectly on one host but not on another?
Is there a specific PHP module that has to be enabled/installed for this to work?
Regards
Jason
Hi,
Posted on 2008-03-21 14:02:43I discovered a problem when using the script in
combination with prototype.
The fix is simple ...
file:expand.js line:12
-- OLD --
for(i in cookieA)
-- NEW --
for(i=0;i<cookieA.length;i++)
Prototype adds some generic methods so using the "for in"-iteration causes errors.
cheers
Stefan
Hi,
Posted on 2008-03-21 14:02:43I'm having the same problems as Schalk & Jason whereby only "Web Development" & "Php" are printing. It stops there.
Can anyone else get this to work?
Thanks
Hi. This code is great! But one problem: I copied the code straight from this page, and it works fine, except for one thing. I don't want the category names to be linked. I only want the subcategories to be linked. In the example displayed on this page, the category names are not linked, and this is how I want it, but when I copied the code, it makes them links. How do I fix this?
Posted on 2008-03-21 14:02:43Hi, It looks like this script has a bug. I also has problem listing all of the categories. It starts nicely by listing Web development og followed by Php, but then it stops.
Posted on 2008-03-05 07:58:50It really seems like the loop does stops when the last sub categori does not contain a new sub. Because if you add a new sub cat. it will be listed.
Ok? Some ideas?
Actually i think this has something to do with the use of a global var.
Posted on 2008-05-01 14:22:23If you change the function to
generate_menu($parent, $menu_array) {
remove the line "global $menu_array;"
change the inner recursive call to generate_menu($key, $menu_array)
and the initial call to the recursive function to generate_menu(0, $menu_array) where $menu_array is your mysql result set
it will work!
As php SHOULD make use of pointers and call_by_reference, this will indeed result in a slightly higher memory usage. If it isn't this is just ... going to be exponential large if you have a deeply nested structure. However I'll try this way :)
Also, change the mysqli_whatever calls to mysql_whatever, as mysqli is deprecated.
HTH
Mac
Hi,
Posted on 2008-06-13 15:17:10I was reading your article today and this is exactly what I need for the project I'm working on.
One think though, is there any way that the script is aware of the hierarchy when it displaying the URL as well?
I'll jut give you an example. Say we have
Web development - /web-development
- - Javascript - /web-development/javascript
- - - - AJAX - /web-development/javascript/ajax
Again, very good article and thanks a lot,
titel
This is such a wonderful script....It really helps me to show the category tree like site map.
Posted on 2008-06-26 08:51:54Thanx a lot
Hi, I have downloaded the menu version and am wondering how to implement an image arrow bullet for the parent categories to show there are children while recursive function sorts down the levels. Can't seem to make it work any thoughts??
Posted on 2008-08-21 16:33:14thanks Terry
Very good script, but the article could be a little more in detail explaining the variable $has_childs = false; in relationship to the $menu_array or the if ($value['parent'] == $parent). This would be helpfull for a newbee like me.
Posted on 2008-08-23 23:02:45Make yourself heard