I am trying to create an expandable list. It is showing correctly for 1 node in the following manner:
However, when I do the same for more than 1 items ie. when number of departments are more than 1. It distributes the list anywhere around. Here is my code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Family Tree</title>
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen, projection">
<script type="text/javascript" src="js/jquery-1.4.2.min.js">
</script>
<script type="text/javascript" src="js/scripts.js">
</script>
</script>
</head>
<body>
<h1><b>Demo ExpandableList</b></h1>
<div id="listContainer">
<div class="listControl">
<a id="expandList">Expand All</a>
<a id="collapseList">Collapse All</a>
</div>
<ul id="expList">
<?php
echo '<li>';
echo 'University';
for($department = 0; $department < 3; $department++){
if($department == 0){
echo '<ul>';
echo '<li>';
echo "d".$department;
}
else if($department == $countOfDepartments - 1){
echo '</li>';
echo '</ul>';
} else{
echo '<li>';
echo "d".$department;
}
for($course = 0; $course < 3; $course++){
if($course == 0){
echo '<ul>';
echo '<li>';
echo "c".$course;
}
else if($course == $countOfCourses - 1){
echo '</li>';
echo '</ul>';
} else{
echo '<li>';
echo "c".$course;
}
for($student = 0; $student < 3; $student++){
if($student == 0){
echo '<ul>';
echo '<li>';
echo "s".$student;
}
else if($student == $countOfStudents - 1){
echo '</li>';
echo '</ul>';
} else{
echo '<li>';
echo "s".$student;
}
} // for - 3
} // for - 2
}// for - 1
echo '</li>';
?>
</ul>
</div>
</body>
</html>
styles.js:
function prepareList() {
$('#expList').find('li:has(ul)')
.click( function(event) {
if (this == event.target) {
$(this).toggleClass('expanded');
$(this).children('ul').toggle('medium');
}
return false;
})
.addClass('collapsed')
.children('ul').hide();
//Create the button funtionality
$('#expandList')
.unbind('click')
.click( function() {
$('.collapsed').addClass('expanded');
$('.collapsed').children().show('medium');
})
$('#collapseList')
.unbind('click')
.click( function() {
$('.collapsed').removeClass('expanded');
$('.collapsed').children().hide('medium');
})
};
$(document).ready( function() {
prepareList()
});
style.css:
body {
font-size: 16px;
}
#menu {
list-style: none;
padding: 0;
margin: 0;
}
.clear {
clear: both;
}
/********************/
/* EXPANDABLE LIST */
/********************/
#listContainer{
margin-top:15px;
}
#expList ul, li {
list-style: none;
margin:0;
padding:0;
cursor: pointer;
}
#expList p {
margin:0;
display:block;
}
#expList p:hover {
background-color:#121212;
}
#expList li {
line-height:140%;
text-indent:0px;
background-position: 1px 8px;
padding-left: 20px;
background-repeat: no-repeat;
}
/* Collapsed state for list element */
#expList .collapsed {
background-image: url(../img/collapsed.png);
}
/* Expanded state for list element
/* NOTE: This class must be located UNDER the collapsed one */
#expList .expanded {
background-image: url(../img/expanded.png);
}
#expList {
clear: both;
}
.listControl{
margin-bottom: 15px;
}
.listControl a {
border: 1px solid #555555;
color: #555555;
cursor: pointer;
height: 1.5em;
line-height: 1.5em;
margin-right: 5px;
padding: 4px 10px;
}
.listControl a:hover {
color:#222222;
font-weight:normal;
}
The list that I am trying to get is in the following format;
What am I possibly doing wrong? My logic seems to be correct.
Broken code: PHP errors
Your code fires errors like
"Notice: Undefined variable:
countOfStudents
"
(same with countOfCourses
and countOfDepartments
) because you didn't define these variables:
for ($department = 0; $department < 3; $department++) {
if ($department == 0) {
//...
else if ($department == $countOfDepartments - 1) {
Instead it should be something like this (assuming $countOfDepartments
is previously defined):
for ($department = 0; $department < $countOfDepartments; $department++) {
if ($department == 0) {
//...
else if ($department == $countOfDepartments - 1) {
Code not working as expected
For now if we merely replace the undefined variables by 3
, now it works without error, but it lacks opening last occurrence of departments, courses, and students, like this:
As illustrated below for departments, your code successively:
opens <ul>
for university, then <li>
for department #0:
if ($department == 0) {
echo '<ul>';
echo '<li>';
echo "d".$department;
}
closes <li>
for the (last) department #2, then <ul>
for university:
else if ($department == 3 - 1) {
echo '</li>';
echo '</ul>';
opens <li>
for any other department (here #1):
} else {
echo '<li>';
echo "d".$department;
}
You can notice that it lacks to open last department and last course, and doesn't display last student, not to mention close all departments but last one (fortunately the browser manages that).
How to make it work
So the above was for explaining why you don't get what you're expecting. But I'll not propose you a way to correct your solution, since it is much too complicated and not the most efficient one.
Instead you might use a recursive method, like this:
function array2ul($array) {
$return = NULL;
foreach ($array as $key => $value) {
$return .=
'<li>' . $key . (is_array($value) ? array2ul($value) : $value) . '</li>';
}
return '<ul>' . $return . '</ul>';
}
Then the whole university <li>
becomes:
echo array2ul([
'd0' => [
'c0' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
'c1' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
'c2' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
],
'd1' => [
'c0' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
'c1' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
'c2' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
],
'd2' => [
'c0' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
'c1' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
'c2' => ['s0' => NULL, 's1' => NULL, 's2' => NULL,],
],
]);
At this stage you might say that this solution uses a very simple function but needs a somewhat heavy previous definition!
It's because I guess the case you provided is intended only as an example, while for your real application you'll get source data from a database, so you'll have to adapt the source data (and maybe the function) to the real structure you get.
Anyway, at least for fun, let's turn the current example into a yet simple form. You can generate the above structure from the mere expression of your needs, like this:
function generate($groups) {
$name = key($groups);
$number = array_shift($groups);
for ($i = 0; $i < $number; $i++) {
$out[$name . $i] = $groups ? generate($groups) : NULL;
}
return $out;
}
Then the whole university <li>
becomes merely:
echo array2ul(generate(['d' => 3, 'c' => 3, 's' => 3]));
And finally here is your entire HTML code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Family Tree</title>
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen, projection">
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="js/scripts.js"></script>
</head>
<body>
<h1><b>Demo ExpandableList</b></h1>
<div id="listContainer">
<div class="listControl">
<a id="expandList">Expand All</a>
<a id="collapseList">Collapse All</a>
</div>
<ul id="expList">
<li>
University
<?php echo array2ul(generate(['d' => 3, 'c' => 3, 's' => 3])); ?>
</li>
</ul>
</div>
</body>
</html>
<?php
function array2ul($array) {
$return = NULL;
foreach ($array as $key => $value) {
$return .=
'<li>' . $key . (is_array($value) ? array2ul($value) : $value) . '</li>';
}
return '<ul>' . $return . '</ul>';
}
function generate($groups) {
$name = key($groups);
$number = array_shift($groups);
for ($i = 0; $i < $number; $i++) {
$out[$name . $i] = $groups ? generate($groups) : NULL;
}
return $out;
}
It now gives the expected resul: