<?php
/**
* This is an example on how to create an expandable tree menu structure with
* PHP, CSS and jQuery.
*
* @version 2011-Feb-18
*
* @author Marc Ermshaus <http://www.ermshaus.org/>
* @license GNU General Public License <http://www.gnu.org/licenses/gpl.html>
*/
define('X_BASEURL', '/nb/menutest');
define('X_CHARSET', 'UTF-8');
/**
* Assembles an internal URL
*
* @param string $path
* @param array $queryPart
* @return string The assembled URL
*/
function url($path, $queryPart = array())
{
$baseUrl = X_BASEURL;
$url = $baseUrl . $path;
if (count($queryPart) > 0) {
$url .= '?' . http_build_query($queryPart);
}
return $url;
}
/**
* Escapes a string for HTML display
*
* @param strng $s
* @param int $quoteStyle
* @param string $charset
* @return string
*/
function escape($s, $quoteStyle = ENT_QUOTES, $charset = X_CHARSET)
{
return htmlspecialchars($s, $quoteStyle, $charset);
}
/**
* Arranges the input data into a tree structure
*
* @param array $data
* @param int|null $parentId
* @return array
*/
function toTree(array $data, $parentId = null)
{
$rec = function (array $data, array &$root, $parent_id = null) use (&$rec)
{
$root['children'] = array();
foreach ($data as $item) {
if ($item['parent_id'] === $parent_id) {
$newChild = array('data' => $item['data']);
$root['children'][] = &$newChild;
$rec($data, $newChild, $item['id']);
unset($newChild);
}
}
};
$root = array('title' => 'root');
$rec($data, $root, $parentId);
return $root;
}
/**
* Creates the HTML output for a navigation tree
*
* @param array $root Tree root (see toTree function)
* @return string HTML code of navigation
*/
function menuHelper($root)
{
$s = '';
$s .= '<ul id="navigation">' . "\n";
$recm = function (array $node, $depth = 0) use (&$recm)
{
$pad = ' ';
foreach ($node['children'] as $child) {
$spanClasses = 'title ';
if (count($child['children']) > 0) {
$spanClasses .= 'hasChildren ';
}
$spanClasses = trim($spanClasses);
$s .= str_repeat($pad, $depth) . '<li>';
$s .= '<span class="'.$spanClasses.'">';
if ($child['data']['path'] !== null) {
$s .= '<a href="'.url($child['data']['path']).'">'
. escape($child['data']['title']) . '</a>';
} else {
$s .= escape($child['data']['title']);
}
$s .= '</span>';
if (count($child['children']) === 0) {
$s .= '<div style="float: right;"><span>edit</span> <span>delete</span></div>';
$s .= '<p>Additional info.</p>';
}
if (count($child['children']) > 0) {
$s .= "\n";
$depth++;
$s .= str_repeat($pad, $depth) . '<ul>' . "\n";
$s .= $recm($child, $depth + 1);
$s .= str_repeat($pad, $depth) . '</ul>' . "\n";
$depth--;
$s .= str_repeat($pad, $depth);
}
$s .= '</li>' . "\n";
}
return $s;
};
$s .= $recm($root, 1);
$s .= '</ul>' . "\n";
return $s;
}
$menuData = array(
array('id' => 1,
'parent_id' => null,
'data' => array('title' => 'Item 1',
'path' => '/item1')),
array('id' => 2,
'parent_id' => null,
'data' => array('title' => 'Item 2',
'path' => '/item2')),
array('id' => 3,
'parent_id' => 1,
'data' => array('title' => 'Item 1.1',
'path' => '/item1/item1')),
array('id' => 4,
'parent_id' => 2,
'data' => array('title' => 'Item 2.1',
'path' => '/item2/item1')),
array('id' => 5,
'parent_id' => 2,
'data' => array('title' => 'Item 2.2',
'path' => '/item2/item2')),
array('id' => 6,
'parent_id' => 1,
'data' => array('title' => 'Item 1.2',
'path' => '/item1/item2')),
array('id' => 7,
'parent_id' => 4,
'data' => array('title' => 'Item 2.1.1',
'path' => '/item2/item1/item1')),
array('id' => 8,
'parent_id' => null,
'data' => array('title' => 'Item 3',
'path' => '/item3'))
);
?><!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Expandable tree menu</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<style type="text/css">
ul, li {
margin: 0; padding: 0;
}
ul ul {
padding-left: 2em;
}
li p {
padding: 0.5em 1em;
margin: 0;
}
#navigation li {
display: block;
background: #eee;
border-top: 1px solid #ccc;
}
#navigation .title {
cursor: pointer;
}
#navigation .hasChildren {
/*background: #9ff;*/
}
#navigation .hasChildren:after {
content: " (expand)";
}
#navigation .hidden {
display: none;
}
#navigation .open-category {
}
/* "#navigation .open-category > .title" or
"#navigation .title.open" won't work in IE6 */
#navigation .open-title {
color: #f00;
}
</style>
<script type="text/javascript">
$(document).ready(function () {
$('#navigation ul').addClass('hidden');
$('#navigation li').click(function (event) {
// Entry has children?
if ($(this).children('ul').length > 0) {
$(this).toggleClass('open-category');
$(this).children('.title').toggleClass('open-title');
$(this).children('ul').toggleClass('hidden');
}
event.stopPropagation();
});
});
</script>
</head>
<body>
<?php echo menuHelper(toTree($menuData)); ?>
</body>
</html>