Click to See Complete Forum and Search --> : heirarchical selection menu
azazel
01-04-2004, 05:05 PM
hi!
here's the situation:
an outline format list of files that the user can pick to download. each file has a checkbox. i want the user to be able to select all the files in a sublevel by clicking the checkbox for that level.
visualize:
(x shall be checkbox)
x top level
x sub level
x bottom level
x bottom level
x sub level
x bottom level
x bottom level
clicking the top level would select everything. clicking a sub level would select the files underneath that level. clicking a bottom level would only select that file.
also, i'd like it to work backwards too. if a user deselects a file on the bottom level, the sublevel directly above that should uncheck.
is this even possible with javascript? have i bitten off more than i can chew? if so, suggestions for a more elegant way to approach this would be appreciated.
thanks in advance!
attached is a bare beginning...
olerag
01-04-2004, 05:35 PM
I quickly looked at your attachment and the naming
convention you've used may not be what you want as some
of the checkboxes have (even in each subgroup) identical
names.
You'll also probably need to iterate thru your checkboxes,
especially during your "reverse" action.
I can get a code sample to you tomorrown morning (EST) if
this is not too late unless someone else provides something
sooner.
A couple other questions:
1. Is this the extent of the qty of checkboxes you'll be using
or should the code handle this dynamically?
2. Do you need assistance with retrieving the
corresponding URL values for your download?
olerag
01-05-2004, 09:56 AM
This should handle your checkbox control. Just adhere to
the checkbox naming convention - if altered, you'll need to
adjust the code accordingly.
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=ISO-8859-1">
<script type="text/javascript">
function dlCheck(cb) {
var objName = cb.name;
var state = cb.checked;
if (objName == "dltop")
controlOthers(state, "dltop");
else {
document.forms[0].dltop.checked = false;
if (objName.substring(0,5) == "dlgrp")
controlOthers(state,objName.substring(5,6));
else {
for (var i=0; i<document.forms[0].length; i++) {
if (document.forms[0].elements[i].name == "dlgrp" + objName.substring(2,3))
document.forms[0].elements[i].checked = false;
}
}
}
}
function controlOthers(state, nameVal) {
var obj;
for (var i=0; i<document.forms[0].elements.length; i++) {
obj = document.forms[0].elements[i];
if (obj.type == "checkbox") {
if (nameVal == "dltop") {
if (obj.name.substring(0,2) == "dl")
obj.checked = state;
}
else {
if (obj.name.substring(0,6) == "dl" + nameVal + "sub")
obj.checked = state;
}
}
}
}
</script>
<title>Checkbox Download Sample</title>
</head>
<body bgcolor="#ffffff">
<b>Checkbox Download Sample</b>
<p>
<form>
<input type="checkbox" name="dltop" value="Top" onClick="dlCheck(this)">Top Level<p>
<input type="checkbox" name="dlgrp1" value="Group 1" onClick="dlCheck(this)">Group 1</br>
<input type="checkbox" name="dl1sub1" value="Group 1 Item 1" onClick="dlCheck(this)">Item 1</br>
<input type="checkbox" name="dl1sub2" value="Group 1 Item 2" onClick="dlCheck(this)">Item 2<p>
<input type="checkbox" name="dlgrp2" value="Group 2" onClick="dlCheck(this)">Group 2</br>
<input type="checkbox" name="dl2sub1" value="Group 2 Item 1" onClick="dlCheck(this)">Item 1</br>
<input type="checkbox" name="dl2sub2" value="Group 2 Item 2" onClick="dlCheck(this)">Item 2
</form>
</body>
</html>
azazel
01-05-2004, 09:59 AM
thanks for replying!
the reason the names of the subgroups are the same is so that "document.form.group" will include every checkbox in that subgroup. i changed the names that are the same to 'id=' instead. is that better, syntactically?
also, keep in mind that this example is just that. so i'd different names and so on in the final code.
yes, all the checkboxes will be generated dynamically. so no, the quantity is not set.
as far as the downloading goes, i have my php handle that along with the database. the main issue i have with downloading is that i want it to compress all the files into a zip before downloading. i got it to work great on a linux box, but i need to make it run on a windows machine. i think that would be a different topic for a different forum, though. hm... i think i will post that question to the PHP topic...
i would love to have any code samples you might have! i'm learning this as i go, if you couldn't tell, so any code (as long as it works ;) ) is appreciated.
thanks!
azazel
01-05-2004, 10:01 AM
what're the odds? we were posting at the same time. thanks for the code! that looks like it might work. testing....
olerag
01-05-2004, 10:10 AM
To obtain your URL values, you can assign and retrieve them
from the "value" attribute of each checkbox.
On your actual "download" object (probably a ?button?),
retrieve all "Item" checkboxes that are checked and place
those values ?in an array?. If the array length is > 0, then
a download is indicated.
The actual download process, I suppose, you've already
figured out?
Oh, and on your <script> tag, alter it as...
<script type="text/javascript">
azazel
01-05-2004, 10:20 AM
thank you!! it works!
the only thing that still doesn't work entirely like it's supposed to is the top level checkbox. it unchecks like a dream when one of the subgroups is deselected, but won't recheck when all checkboxes underneath it are checked. obviously, that would take a lot more work... because you'd have to make sure everything below it is checked... is there any easy way to do that? or... a way at all?
thanks again for all your help! :)
azazel
01-05-2004, 10:41 AM
sorry, but i just realized that the code you gave me will only work with one top level checkbox. i wasn't thinking. that should be pretty easy to fix, though, right?
i'm working on it right now, but if you (or anybody) have a solution, that would be great! thanks again!
olerag
01-05-2004, 10:50 AM
I know. And note the "groups" don't auto come on if all
items are selected. This is because the user has already
tinkered with the checkboxes. The key is that the items
that represent the actual download have the correct
indication.
This may not be a good reason but it was mine at the time
the code was prepared.
To include this (non-essential) extra, you'll need to loop
thru again if an "item" is selected and check if all of those
group items are checked. If so, then check the Group
checkbox. Then, if this is done, loop thru all Group
checkboxes and if all are on, turn on the Top checkbox.
So, this would entail two extra loops and would kick off
after the "for" loop in the last "else" clause of the
"dlCheck()" function.
Do you want to try this or do you want me to play with it??
azazel
01-05-2004, 11:07 AM
if you don't mind, i'd much appreciate as much help as you can give me. :) so, yes, if you would try the looping to uncheck or check the top level checkboxes when all the sub checkboxes are changed, that would be grand.
however, it's more important that i get this to work with more than one top level checkbox. i'm working on this now, but i'm feeling woefully underexperienced. :(
thanks!
olerag
01-05-2004, 11:25 AM
OK, try this. Just cut/paste the JS code below into the existing
HTML code. And I'd forget multiple "tops"; simple include
new "groups" - as long as you maintain the naming
convention, you can add as many as you like.
<script type="text/javascript">
function dlCheck(cb) {
var objName = cb.name;
var state = cb.checked;
if (objName == "dltop")
controlOthers(state, "dltop");
else {
document.forms[0].dltop.checked = false;
if (objName.substring(0,5) == "dlgrp")
controlOthers(state,objName.substring(5,6));
else {
for (var i=0; i<document.forms[0].length; i++) {
if (document.forms[0].elements[i].name == "dlgrp" + objName.substring(2,3))
document.forms[0].elements[i].checked = false;
}
if (state)
checkGroupLevels(objName.substring(2,3));
}
if (state)
checkTopLevel();
}
}
function controlOthers(state, nameVal) {
var obj;
for (var i=0; i<document.forms[0].elements.length; i++) {
obj = document.forms[0].elements[i];
if (obj.type == "checkbox") {
if (nameVal == "dltop") {
if (obj.name.substring(0,2) == "dl")
obj.checked = state;
}
else {
if (obj.name.substring(0,6) == "dl" + nameVal + "sub")
obj.checked = state;
}
}
}
}
function checkGroupLevels(groupVal) {
var obj;
var success = true;
for (var i=0; i< document.forms[0].elements.length; i++) {
obj = document.forms[0].elements[i];
if (obj.name.substring(0,6) == "dl" + groupVal + "sub") {
if (!obj.checked) {
success = false;
break;
}
}
}
if (success) {
for (var i=0; i<document.forms[0].elements.length; i++) {
if (document.forms[0].elements[i].name == "dlgrp" + groupVal) {
document.forms[0].elements[i].checked = success;
break;
}
}
}
}
function checkTopLevel() {
var obj;
var success = true;
for (var i=0; i< document.forms[0].elements.length; i++) {
obj = document.forms[0].elements[i];
if (obj.name.substring(0,5) == "dlgrp") {
if (!obj.checked) {
success = false;
break;
}
}
}
document.forms[0].dltop.checked = success;
}
</script>
azazel
01-05-2004, 11:35 AM
thanks!
i would forget multiple tops... except that for the application i'm writing, that's a requirement. in other words, i need an outline style list, with three levels.
right now, i'm working on modifying the code to allow a naming convention like this:
dltop001
dl001grp001
dl001grp001sub001
dl001grp002
dl001grp002sub001
dl001grp002sub002
dltop002
dl002grp001
dl002grp001sub001
...and so on.
the zeros being placeholders, so that substring will work with larger numbers.
olerag
01-05-2004, 11:45 AM
Ouch - your hierarchy "tree" in your first post (single top)
is different than your last post (multiple "branches/limbs" with
no "single top").
You'll need to adjust the code accordingly but I hope you've
been given enough suggestions so you can complete your
task.
azazel
01-05-2004, 02:55 PM
sorry, i just forgot to mention that in the first example. <sheepish grin>
thanks for all your help!
i will try to take it from here; i think you've helped me enough that i can get it working.
thanks again. :)
azazel
01-05-2004, 03:41 PM
ok, i got some help over at codingforums.com as well: this is the code he provided:
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=ISO-8859-1">
<script>
function check(f,isChild) {
/* Returns an array with children inputs */
function getChildren(f) {
var childContainer = f.nextSibling;
while (childContainer && childContainer.tagName != "BLOCKQUOTE") {
childContainer = childContainer.nextSibling;
}
if (childContainer) {
return childContainer.getElementsByTagName("INPUT");
} else {
return [];
}
}
/* Checks / unchecks all parents */
function checkParent(f,checked) {
var p=f.parentNode.previousSibling;
while (p && p.tagName!="INPUT") {
p=p.previousSibling;
}
if (p) {
var fields=getChildren(p);
for (var i = 0; i < fields.length; i++) {
if (!fields[ i ].checked) {
checked=false;
}
}
p.checked=checked;
checkParent(p,checked);
}
}
var checked=f.checked;
/* Check / uncheck all children */
var fields=getChildren(f);
for (var i = 0; i < fields.length; i++) {
fields[ i ].checked = checked;
check(fields[ i ],true);
}
/* Verify the state of the parents to calling node */
if (typeof isChild=="undefined") {
checkParent(f,checked);
}
}
</script>
<title>Test JS</title>
</head>
<body>
<form name="form">
<input type="checkbox" onclick="check(this);" />top level<br />
<blockquote>
<input type="checkbox" onclick="check(this);" />sublevel<br />
<blockquote>
<input type="checkbox" onclick="check(this);" />bottom level<br />
<input type="checkbox" onclick="check(this);" />bottom level
</blockquote>
<input type="checkbox" onclick="check(this);" />sublevel<br />
<blockquote>
<input type="checkbox" onclick="check(this);" />bottom level<br />
<input type="checkbox" onclick="check(this);" />bottom level
</blockquote>
</blockquote>
<input type="checkbox" onclick="check(this);" />top level<br />
<blockquote>
<input type="checkbox" onclick="check(this);" />sublevel<br />
<blockquote>
<input type="checkbox" onclick="check(this);" />bottom level<br />
<input type="checkbox" onclick="check(this);" />bottom level
</blockquote>
<input type="checkbox" onclick="check(this);" />sublevel<br />
<blockquote>
<input type="checkbox" onclick="check(this);" />bottom level<br />
<input type="checkbox" onclick="check(this);" />bottom level
</blockquote>
</blockquote>
</form>
</body>
</html>
it works with two top levels! the only problem now is... in my application, i'm not actually using blockquotes to format my list. :( so.... now i need to find out how to modify this so it'll work with <div>'s. or else modify my PHP to format with blockquotes.
olerag
01-05-2004, 05:12 PM
Certainly looks like another way to proceed and if it works
and you like it then I'd say go with it.
The only problem now is... in my application, i'm not actually
using blockquotes to format my list. so.... now i need to find
out how to modify this so it'll work with <div>'s. or else modify
my PHP to format with blockquotes
Probably the later (altering your html via php) would be
easier unless your big on CSS.
azazel
01-06-2004, 07:07 PM
i ended up using this script, and just altering my PHP so that it represented the parent child relationship better, but still using divs.
function check(f,isChild) {
/* Returns an array with children inputs */
function getChildren(f) {
var childContainer = f.nextSibling;
while (childContainer && childContainer.tagName != "DIV") {
childContainer = childContainer.nextSibling;
}
if (childContainer) {
return childContainer.getElementsByTagName("INPUT");
} else {
return [];
}
}
/* Checks / unchecks all parents */
function checkParent(f,checked) {
var p=f.parentNode.previousSibling;
while (p && p.tagName != "INPUT") {
p = p.previousSibling;
}
if (p) {
var fields=getChildren(p);
for (var i = 0; i < fields.length; i++) {
if (!fields[ i ].checked) {
checked = false;
}
}
p.checked = checked;
checkParent(p,checked);
}
}
var checked = f.checked;
/* Check / uncheck all children */
var fields = getChildren(f);
for (var i = 0; i < fields.length; i++) {
fields[ i ].checked = checked;
check(fields[ i ],true);
}
/* Verify the state of the parents to calling node */
if (typeof isChild == "undefined") {
checkParent(f,checked);
}
}
(thanks to Danne over at CodingForum.com for this script)
my final source for one section of the list looked something like this:
<div class="section"><input type="checkbox" onclick="check(this);" /><span class="sectiontitle">top level</span><br />
<div class="subsection">
<input type="checkbox" onclick="check(this);" /><span class="subsectiontitle">sublevel</span><br />
<div class="subfile"><span>
<input type="checkbox" onclick="check(this);" />bottom level<br />
<input type="checkbox" onclick="check(this);" />bottom level</span>
</div>
<input type="checkbox" onclick="check(this);" /><span class="subsectiontitle">sublevel</span><br />
<div class="subfile">
<input type="checkbox" onclick="check(this);" />bottom level<br />
<input type="checkbox" onclick="check(this);" />bottom level
</div>
</div>
</div>
hope this helps some other folks! :)