How to display inifinit depth expandable categories using php and javascript

2007-09-29

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

kasp3r

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

Posted on 2007-11-19 08:53:07
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-07 08:25:25
dod

thanks for the clear way you have put this tut together.
just the facts none of the crap.
I like how you mainly focused on what we really want to know, the php

Posted on 2007-11-12 07:31:30
Jason

Hi,

I have tried using the above on two different hosts - both are running php v5.

On one host, the menu displays perfectly but on the other it only ever shows the first 'parent' and first 'child'.

Is there a specific PHP Module that needs to be enabled for this menu to work? (hosts question not mine :-))

Many thanks

Kind Regards

Jason

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

Hi Jason, I had complains about this problem from other users, there is no php module requirement, I didn't discover the cause that is producing this problem.
I just retested my script and it works fine, I'm using php 5.2.4 with apache on linux for my tests, but I think it works fine on older versions to.

Posted on 2007-11-26 07:32:29
Jason

Hi,

Strange - I've also tried example above from Rik Moncur and it does exactly the same, works on one host but not another.

Regards

Jason

Posted on 2007-11-26 07:32:56
Mauro

Hi, the problem is that you need to select the rows inside the function instead of declaring the menu_array as a global. It should work after that.

Posted on 2007-11-26 07:31:52
Jason

Cheers Mauro - you are a star. It now works a treat :-))

Posted on 2007-11-26 07:50:36
Mel

Hi Mauro, I have the same problem as Jason but I dont understand you explination. What does this mean "you need to select the rows inside the function instead of declaring the menu_array as a global."? What does that mean? Which part of the script must I edit? Can you please give us an example.

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

This code is great. I'm using the one with CSS formatting, not the javascript expandable one. My problem is the category names are linked and I don't want them to be. I only want the subcategories to be linked. On this page, the example shows the categories as unlinked, but when I use the code, it makes my category names linked. Any ideas?

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

What about deleting? No one ever shows about deleting. I'm not talking DELETE WHERE id = $id AND parent = $id I'm talking if theres a Parent with a Child(1) that has a child(2) that has a child(3). If you delete the main parent how would it go about deleting all the way up to child 3 as child 2 is it's parent and child 1 is child 2's parent and parent is the parent of child 1. Hope I didn't confuse anyone.

Posted on 2008-05-01 14:21:24
Jeremy

First - thanks for the tutorial. Mauro, I had the same problem (worked on one server but only first row shows on a different server). Can you explain what you mean by selecting the rows inside the function instead of declaring the menu_array as global? Thanks.

Posted on 2008-04-18 00:57:45
Nguyen Duc

Hi, I tried using a $var just replace "echo" in this function but can't. Help me because I am using Xtemplate for my project.
Thanks

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

Please provide an example of what you mean by "you need to select the rows inside the function instead of declaring the menu_array as a global." I have had the same issue with only one set of results being displayed.

Thank you.

Posted on 2008-05-01 14:22:23
Nguyen Duc

Hi, I tried using a $var just replace "echo" in this function but can't. Help me because I am using Xtemplate for my project.
Thanks

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

Hi, Good Worked. I can\'t able to list the display in ul li order. with the help of your code, i can.


Thanks

Posted on 2008-07-06 03:46:39

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