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 15: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 15: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 14: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 20: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 14: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 14: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 14:31:52
Jason

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

Posted on 2007-11-26 14: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 20: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 20: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 21: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 07: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 21: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 21: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 21: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 10:46:39
Thomas

Hi, I enjoyed your tutorial. However with a category structure of up to 3000 different rows. The code does run slow.

Posted on 2009-02-12 11:30:44
Zaheer

Hy Barry, By "you need to select the rows inside the function instead of declaring the menu_array as a global" he means to remove global declaration of array from the function definition and pass it to the function as a argument.

Posted on 2009-07-01 12:58:12
Jason

Hi, thanks so much for this code; I am writing a file manager component for Joomla and I couldn't figure out how to manage unlimited sub-categories cleanly, and with only using one database query.

Posted on 2009-07-13 08:59:25
Techie Talks

Great codes, I am really having a hard time displaying the cat subcat that I have in a way it will show up like a combo box. Thank you!

Posted on 2009-11-21 06:58:48
Someone Nordic

You have just solved my 2 day-old vb headache with that recursive function. Awesome job

Posted on 2010-03-03 13:46:23
Rob @ Web Design Talk

GReat piece of code. Tested with a very very large category table and things are little slow. However for my shop that has 4 levels of categories this is absolutely perfect.

Thanks

Posted on 2010-03-08 20:13:08
kevs

Is it possible, using this code, to output a categories and it's subCat based on a specific ID?

Web Developement ID is 1. I want to fetch only Web Developement and it's child.

How do I do that?

Please help

Posted on 2010-03-25 17:11:28
Chintan

Hi Thanks for sharing program.but how can i display all parent & child categories in dropdown with differentiation..
like
Parent1<br>
---Childparent1<br>
Parent2<br>
---Child1parent2<br>
------childchild1parent2

Posted on 2010-09-03 12:51:01
Mahi

Hi,
really nice code but I want a multi dimension array one row for one record. but I can't able to do this I am new in php can you help me. actually I want to show this data in a list format.
regards,
Mahi,

Posted on 2010-12-18 09:45:46
Balone

Hi,
this worked perfectly. can you tell me how to worked this with combobox?(dropdown)

Thanks for any reply.

Posted on 2011-01-10 13:15:34
babar

very good example.i just want to add just two lines.

1. add id field also

$menu_array[$row['id']] = array('name' => $row['name'],'parent' => $row['parent'],'link' => $row['link'],'id'=>$row['id']);

2. make it dynamic

echo '<li><a href="products.php?cid='. $value['id'] . '">' . $value['name'] . '</a>';

thanks

Posted on 2011-07-01 02:53:46
Arif Majid

Works perfectly...
one single query ... print all the categories and its subcategories(multiple level)..

But if its a huge category list i wud rather cache the output in a file and fetch it from there,
so that i dont have to use this function
unless there was a change in the (CRUD operation done on the category table) categories list.

thanks
Arif

Posted on 2011-09-14 13:22:32
Anuj Koundal

Hi
Thanks Codeassembly for such an easy code It works for Asp .net C# Tooo

Thanks

For ASP .net guys here is my code:

Bind dataset or genric list with data and follow the code.

string treeList = string.Empty;
public string BindEmp(int ParentId)
{
Boolean hasChild = false;
foreach (DataRow dr in ds.Tables[0].Rows)
{
if (ParentId == Convert.ToInt32(dr["projEmp_ParentEmpId"]))
{

if (hasChild == false)
{
hasChild = true;
treeList += "<ul>";

}
treeList += "<li>" + dr["empname"].ToString();

BindEmp(Convert.ToInt32(dr["projEmp_EmpId"]));

treeList += "</li>";

}

}

if (hasChild == true)
{
treeList += "</ul>";
}

return treeList;
}

Hope this help someone.


Thanks
Anuj Koundal

Posted on 2011-12-23 10:19:04
Anuj

Hi
Need your some more help. This code works fine if one category is inserted only once. But how do we achieve if we insert category more than once.

Like

WebDevelopment
HTML

MISC
HTML
HTML1
HTML2
HTML3


Other
HTML

here HTML lies three times.

If you have some time please help.

Posted on 2011-12-26 12:53:58
Ben

Very nice indeed. But I'd prefer not to use globals, so this is my adaptation:

<code>
function generate_menu($parent,$menu_array=null) {
$has_childs = false;
foreach($menu_array as $key => $value) {
if ($value['parent'] == $parent) {
if ($has_childs === false) {
$has_childs = true;
echo "<ul>\n";
}
echo '<li><a href="/category/' . $value['name'] . '/">' . $value['name'] . '</a>';
generate_menu($key,$menu_array);
echo "</li>\n";
}
}
if ($has_childs === true) echo "</ul>\n";
}
</code>

Posted on 2012-02-28 19:48:56
Arcticwarrio

to everyone having trouble alter your query from

$query = mysqli_query($link,'SELECT * FROM categories');

to

$query = mysqli_query($link,'SELECT * FROM categories ORDER BY parent');

Posted on 2013-03-06 12:00:13
jitu

Hi
Need your some more help. This code works fine if one category display category and subcategory as menu. but i want to display product after click on product... plz help me...

Posted on 2013-02-15 08:42:10

Make yourself heard

Categories

Subscribe

All Posts

All Comments

© Copyright CodeAssembly

All code is licensed under LGPL, unless otherwise noted

littlebubu