How to display infinite depth expandable categories using php and javascript

2007-09-30

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

Jason

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?

Hope that makes sense - great tutorial though :-)

Regards

Jason

Posted on 2007-10-05 16:02:58
CodeAssembly

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.
Thanks.

Posted on 2007-11-19 08:53:44
kasp3r

Thank you very much for your article. It helped me a lot!

Posted on 2007-11-19 08:52:58
Schalk

Hi,

I 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

Posted on 2007-11-09 08:39:34
Jason

Hi

Having 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

Posted on 2007-11-26 07:32:42
Stefan

Hi,

I 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

Posted on 2008-03-21 14:02:43
Eric

Hi,

I'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

Posted on 2008-03-21 14:02:43
Mk

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:43
Ken

Hi, 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.

It 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?

Posted on 2008-03-05 07:58:50
MacGoerk

Actually i think this has something to do with the use of a global var.

If 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

Posted on 2008-05-01 14:22:23
titel

Hi,

I 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

Posted on 2008-06-13 15:17:10
Ashish

This is such a wonderful script....It really helps me to show the category tree like site map.
Thanx a lot

Posted on 2008-06-26 08:51:54
Terry

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??
thanks Terry

Posted on 2008-08-21 16:33:14
rocbar

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:45

Make yourself heard

Categories

Subscribe

All Posts

Php posts

All Comments

This post comments

© Copyright CodeAssembly

All code is licensed under GPL, unless otherwise noted