PDA

View Full Version : Cascading Dropdown from CSV



jpsider
06-20-2013, 01:50 PM
Hello,

I'm working on a project that requires a cascading dropdown. I found this site and this proves to be a viable solution (http://www.javascriptkit.com/script/script2/triplecombo.shtml). I have edited the code to make it 4 boxes/options. The one part I am having trouble with is that my data set is large. and creating the dynamic piece of each match looks like it may be cumbersome. I wrote a little batch file (could have done it in any language I guess that will create the lines in the java script. I am looking for some help to maybe modify this code to read a .csv and populate the dropdowns dynamically. I would prefer not to use a DB, the CSV is optimal due to the way I receive the data and the frequency at which it updates (roughly 21 days) so it would be easier to upload a .csv, than to update a DB, or update the javascript.

Thoughts? Does this post need to be moved?

Thanks,

Justin

vwphillips
06-21-2013, 12:22 PM
Have you considered using an external JS file?

the script you link to uses arrays

these arrays could be defined in a .js file which would be loaded to the page when the page loads

these arrays could be updated in the .js file as easily as producing a .CSV file

http://www.javascriptkit.com/javatutors/external.shtml

if this is not acceptable please provide an example of your .CSV file

clueful
06-21-2013, 03:00 PM
Presumably your CSV file must be formatted in such a way that the option hierarchy can be interpreted. If so I
think the most efficient solution would be to create a simulated database using your CSV file, combined with a server side script that reads an id/value querystring sent from a client script, then generates the corresponding table of data for the client to download. That way you don't have to download your entire data set.
If you can implement a suitable server-side interface, a suitable client-side script would be http://scripterlative.com/files/selectcascade.htm , which whenever a selection is made, sends an ID/value querystring to your specified script, expecting to receive a comma-delimited table in reply.

jpsider
06-24-2013, 06:59 PM
Ok, Great suggestions. I was able to write a quick batch script that converts each line of CSV to the html/javascript. I wont be working with that much data (I think) so I will save that conversion/database/csv import for a later date. I am going to paste the code below, but the problem I am having is that my 4th box does not update when I change the selection from the second or third drop-down.

Here is what I have - Courtesy the link I shared earlier... (http://javascriptkit.com/script/script2/triplecombo.shtml)



<html>
<head>
<script type="text/javascript">

/*
Triple Combo Script Credit
By Philip M: http://www.codingforums.com/member.php?u=186
Visit http://javascriptkit.com for this and over 400+ other scripts
*/

var categories = [];
categories["startList"] = ["Apple","Samsung"]
categories["Apple"] = ["Iphone 5 16GB","Iphone 5 32GB","Iphone 5 64GB","Iphone 4S 16GB","Iphone 4S 32GB","Iphone 4S 64GB","Iphone 4 8GB","Iphone 4 16GB","Iphone 4 32GB"];

categories["Iphone 5 16GB"] = ["ATT","Verizon","Tmobile","Sprint"];
categories["ATT"] = ["NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];
categories["Verizon"] = ["NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];
categories["Tmobile"] = ["NewInBox - $350.00","Good - $300.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"];
categories["Sprint"] = ["NewInBox - $350.00","Good - $280.00","Cracked - $280.00","Broken - $170.00","Dead - $140.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 5 32GB"] = ["ATT","Verizon","Tmobile","Sprint"];
categories["ATT"] = ["NewInBox - $600.00","Good - $420.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];
categories["Tmobile"] = ["NewInBox - $360.00","Good - $310.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"];
categories["Sprint"] = ["NewInBox - $360.00","Good - $290.00","Cracked - $280.00","Broken - $170.00","Dead - $150.00"];
categories["Verizon"] = ["NewInBox - $600.00","Good - $420.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 5 64GB"] = ["ATT","Verizon","Tmobile","Sprint"];
categories["Verizon"] = ["NewInBox - $700.00","Good - $440.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];
categories["ATT"] = ["NewInBox - $700.00","Good - $440.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];
categories["Tmobile"] = ["NewInBox - $370.00","Good - $320.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"];
categories["Sprint"] = ["NewInBox - $370.00","Good - $300.00","Cracked - $280.00","Broken - $170.00","Dead - $160.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 4S 16GB"] = ["ATT","Verizon","Sprint"];
categories["ATT"] = ["NewInBox - $400.00","Good - $280.00","Cracked - $250.00","Broken - $200.00","Dead - $90.00"];
categories["Sprint"] = ["NewInBox - $270.00","Good - $170.00","Cracked - $150.00","Broken - $120.00","Dead - $75.00"];
categories["Verizon"] = ["NewInBox - $270.00","Good - $210.00","Cracked - $170.00","Broken - $140.00","Dead - $75.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 4S 32GB"] = ["ATT","Verizon","Sprint"];
categories["Sprint"] = ["NewInBox - $280.00","Good - $170.00","Cracked - $150.00","Broken - $120.00","Dead - $75.00"];
categories["Verizon"] = ["NewInBox - $280.00","Good - $210.00","Cracked - $170.00","Broken - $140.00","Dead - $75.00"];
categories["ATT"] = ["NewInBox - $410.00","Good - $280.00","Cracked - $250.00","Broken - $200.00","Dead - $90.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 4S 64GB"] = ["ATT","Verizon","Sprint"];
categories["Verizon"] = ["NewInBox - $290.00","Good - $210.00","Cracked - $170.00","Broken - $140.00","Dead - $75.00"];
categories["ATT"] = ["NewInBox - $420.00","Good - $280.00","Cracked - $250.00","Broken - $200.00","Dead - $90.00"];
categories["Sprint"] = ["NewInBox - $290.00","Good - $170.00","Cracked - $150.00","Broken - $120.00","Dead - $75.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 4 8GB"] = ["ATT","Verizon","Sprint"];
categories["ATT"] = ["NewInBox - $220.00","Good - $160.00","Cracked - $110.00","Broken - $80.00","Dead - $50.00"];
categories["Sprint"] = ["NewInBox - $100.00","Good - $90.00","Cracked - $60.00","Broken - $45.00","Dead - $30.00"];
categories["Verizon"] = ["NewInBox - $100.00","Good - $90.00","Cracked - $60.00","Broken - $45.00","Dead - $30.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 4 16GB"] = ["ATT","Verizon"];
categories["ATT"] = ["NewInBox - $220.00","Good - $160.00","Cracked - $110.00","Broken - $80.00","Dead - $50.00"];
categories["Verizon"] = ["NewInBox - $100.00","Good - $90.00","Cracked - $60.00","Broken - $45.00","Dead - $30.00"];
/* --------------------------------------------------------------------- */
categories["Iphone 4 32GB"] = ["ATT","Verizon"];
categories["Verizon"] = ["NewInBox - $100.00","Good - $90.00","Cracked - $60.00","Broken - $45.00","Dead - $30.00"];
categories["ATT"] = ["NewInBox - $220.00","Good - $160.00","Cracked - $110.00","Broken - $80.00","Dead - $50.00"];
/* --------------------------------------------------------------------- */


/* ------ This is here for a place holder ----- */
categories["Samsung"] = ["Biography","Fiction","Nonfiction"];
categories["Biography"] = ["Contemporay","Historical","Other"];
categories["Fiction"] = ["Science Fiction","Romance", "Thrillers", "Crime"];
categories["Nonfiction"] = ["How-To","Travel","Cookbooks", "Old Churches"];
/* ------ End of place holder ------ */


var nLists = 4; // number of select lists in the set

function fillSelect(currCat,currList){
var step = Number(currList.name.replace(/\D/g,""));
for (i=step; i<nLists+1; i++) {
document.forms['tripleplay']['List'+i].length = 1;
document.forms['tripleplay']['List'+i].selectedIndex = 0;
}
var nCat = categories[currCat];
for (each in nCat) {
var nOption = document.createElement('option');
var nData = document.createTextNode(nCat[each]);
nOption.setAttribute('value',nCat[each]);
nOption.appendChild(nData);
currList.appendChild(nOption);
}
}


function getValue(L4, L3, L2, L1) {
alert("Your selection was:- \n" + L1 + "\n" + L2 + "\n" + L3 + "\n" + L4);
}

function init() {
fillSelect('startList',document.forms['tripleplay']['List1'])
}

navigator.appName == "Microsoft Internet Explorer" ? attachEvent('onload', init, false) : addEventListener('load', init, false);

</script>
</head>
<body>

<form name="tripleplay" action="">
<select name='List1' onchange="fillSelect(this.value,this.form['List2'])">
<option selected>Make a selection</option>
</select>
&nbsp;
<select name='List2' onchange="fillSelect(this.value,this.form['List3'])">
<option selected>Make a selection</option>
</select>
&nbsp;
<select name='List3' onchange="fillSelect(this.value,this.form['List4'])">
<option selected>Make a selection</option>
</select>
&nbsp;
<select name='List4' onchange="getValue(this.value, this.form['List1'].value, this.form['List2'].value, this.form['List3'].value)">
<option selected >Make a selection</option>
</select>
</br>
</form>
</body>
</html>

Could it be a problem with duplicating data values? (I.E. I use "ATT" quite a bit, or all the carriers for that matter). If so, What would be a good fix? Or am I missing something that refreshes the logic to find the correct data?

Any help is appreciated!

Thanks,

Justin

clueful
06-24-2013, 08:45 PM
the problem I am having is that my 4th box does not update when I change the selection from the second or third drop-down.

Works OK for me.

jpsider
06-24-2013, 10:18 PM
Hmmm, while it displays prices, it does not display the correct prices. When I select :
Iphone 5 16GB & ATT
It gives me these options :
"NewInBox - $220.00","Good - $160.00","Cracked - $110.00","Broken - $80.00","Dead - $50.00"
While it should return :
"NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"

So it appears that everything is defaulting to the last set of "categories"

Thoughts?

clueful
06-25-2013, 03:56 AM
So it appears that everything is defaulting to the last set of "categories"

Thoughts?
That's because you have duplicated array declarations, which exposes a serious shortcoming of this script.

Normally I don't contribute to this site's scripts, but I may make an exception to fix this one.

jpsider
06-25-2013, 10:54 AM
I figured, Oh well. Back to the drawing board! I think this might be a viable solution (http://javascriptkit.com/script/script2/multiplecombo.shtml). It might just be more work on my end creating the Items. Thank you for taking a look!

clueful
06-25-2013, 11:49 AM
I figured, Oh well. Back to the drawing board! I think this might be a viable solution (http://javascriptkit.com/script/script2/multiplecombo.shtml). It might just be more work on my end creating the Items. Thank you for taking a look!

I've created a viable apology for the 'triple combo' script. It still needs a bit of work before publishing but here's the working example. It uses a subset of your data, (enough for the first option in each list). You'll see how the array syntax has been changed to control the option hierarchy. All you have to do is add the rest of the data.

Notes:

The data for the first <select> is always hard-coded and is not part of the array.

The first option in each table is a prompt.


<!doctype html>
<html>
<head>
</head>
<body>

<form name="unlimitedSelects" action="">
<select id='List1' name='List1' >
<option selected>Select Manufacturer</option>
<option value='Apple'>Apple</option>
<option value='Samsung'>Samsung</option>
</select>
&nbsp;
<select id='List2' name='List2' >
</select>
&nbsp;
<select id='List3' name='List3' >
</select>
&nbsp;
<select id='List4' name='List4' >
</select>
</br>
</form>

<script type="text/javascript">

var categories = [];

categories["Apple"] = ["Select Apple Device", "Iphone 5 16GB","Iphone 5 32GB","Iphone 5 64GB","Iphone 4S 16GB","Iphone 4S 32GB","Iphone 4S 64GB","Iphone 4 8GB","Iphone 4 16GB","Iphone 4 32GB"];

categories["Apple||Iphone 5 16GB"] = ["Select Carrier", "ATT", "Verizon", "Tmobile", "Sprint"];

categories["Apple||Iphone 5 16GB||ATT"] = ["Select Condition", "NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Apple||Iphone 5 16GB||Verizon"] = ["Select Condition","NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Apple||Iphone 5 16GB||Tmobile"] = ["Select Condition", "NewInBox - $350.00","Good - $300.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"];

categories["Apple||Iphone 5 16GB||Sprint"] = ["Select Condition", "NewInBox - $350.00","Good - $280.00","Cracked - $280.00","Broken - $170.00","Dead - $140.00"];


categories["Samsung"] = ["Select Samsung Device", "Iphone 5 16GB","Iphone 5 32GB","Iphone 5 64GB","Iphone 4S 16GB","Iphone 4S 32GB","Iphone 4S 64GB","Iphone 4 8GB","Iphone 4 16GB","Iphone 4 32GB"];

categories[ "Samsung||Iphone 5 16GB" ] = ["Select Carrier","ATT","Verizon","Tmobile","Sprint"];

categories["Samsung||Iphone 5 16GB||ATT"] = ["Select Condition", "Samsung NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Samsung||Iphone 5 16GB||Verizon"] = ["Select Condition", "Samsung NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Samsung||Iphone 5 16GB||Tmobile"] = ["Select Condition", "Samsung NewInBox - $350.00","Good - $300.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"];

categories["Samsung||Iphone 5 16GB||Sprint"] = ["Select Condition", "Samsung NewInBox - $350.00","Good - $280.00","Cracked - $280.00","Broken - $170.00","Dead - $140.00"];



function MultiSelect( )
{
var theObject = function()
{
this.boxes = {};

theObject.prototype.init = function()
{
var args = MultiSelect.arguments,
inst = this;

this.dataSet = args[ 0 ];

for( var i = 1, box, id; ( id = args[ i ] ); i++ )
{
if( !( box = this.$( id ) ) )
alert( '<select id="' + id + '"> not found' );
else
{
this.boxes.push( box );

if( i > 1 )
box.style.display = 'none';

if( i < args.length - 1 ) /*No handler on last box*/
this.addListener( box, 'change', function( e ){ inst.changeHandler.call( inst, e ); } );
}
}
}

theObject.prototype.$ = function( id )
{
var elem = document.getElementById( id );

return elem && elem.id === id ? elem : null;
}

theObject.prototype.changeHandler = function( evt )
{ 'use strict';
var selector = "", box, opts, thisBox = evt.srcElement || evt.target, nextBox;

for( var idx = 0; ( box = this.boxes[ idx ] ) && this.boxes[ idx ] !== thisBox; idx++ )
selector += ( idx == 0 ? "" : "||" ) + box.value;

selector += ( idx == 0 ? "" : "||" ) + thisBox.value;

for( var i = idx + 1; this.boxes[ i ]; i++ )
{
this.boxes[ i ].options.length = 0;
this.boxes[ i ].style.display = 'none';
}

if( thisBox.selectedIndex > 0 )
{
( nextBox = this.boxes[ idx + 1 ] ).style.display = '';

if( ( opts = this.dataSet[ selector ] ) )
for( var i = 0, len = opts.length; i < len; i++ )
nextBox.options.add( new Option( opts[ i ], opts[ i ] ) );
}
}

theObject.prototype.addListener = function( elem, evt, func )
{
elem.addEventListener ? elem.addEventListener( evt, func, false )
: elem.attachEvent( 'on' + evt, func );
}

this.init( /*28432927636C756566756C27206F6E2044796E616D69634472697665*/ );

}
return new theObject();
}


MultiSelect( categories, 'List1', 'List2', 'List3', 'List4' );

</script>
</body>
</html>Now available here (http://scripterlative.com/files/multiselect.htm) as a free script with documentation.

jpsider
06-25-2013, 01:25 PM
Wow, This is a more elegant way of doing it! Thank you for your time! I'll let you know if I run into any "gotcha's" but so far the transition is smooth. -Justin

jscheuer1
06-26-2013, 01:44 AM
Using (curly brackets here):


var categories = {};

Not (square brackets):


var categories = [];

Would be more correct and still works. The use of square brackets connotes an array (used properly here):


["Select Carrier", "ATT", "Verizon", "Tmobile", "Sprint"]

or a property of an object (used properly for that here):


categories["Apple||Iphone 5 16GB"]

Curly brackets connote an object. Examples:


var categories = {};


var conventionally_attractive = {girl: 'pretty', boy: 'handsome'};


var colors = {dark: ['black', 'navy'], light: ['yellow', 'white']};

So your object could be written (tested and works) as:


var categories = {

"Apple": ["Select Apple Device", "Iphone 5 16GB","Iphone 5 32GB","Iphone 5 64GB","Iphone 4S 16GB","Iphone 4S 32GB","Iphone 4S 64GB","Iphone 4 8GB","Iphone 4 16GB","Iphone 4 32GB"],

"Apple||Iphone 5 16GB": ["Select Carrier", "ATT", "Verizon", "Tmobile", "Sprint"],

"Apple||Iphone 5 16GB||ATT": ["Select Condition", "NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"],

"Apple||Iphone 5 16GB||Verizon": ["Select Condition","NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"],

"Apple||Iphone 5 16GB||Tmobile": ["Select Condition", "NewInBox - $350.00","Good - $300.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"],

"Apple||Iphone 5 16GB||Sprint": ["Select Condition", "NewInBox - $350.00","Good - $280.00","Cracked - $280.00","Broken - $170.00","Dead - $140.00"],


"Samsung": ["Select Samsung Device", "Iphone 5 16GB","Iphone 5 32GB","Iphone 5 64GB","Iphone 4S 16GB","Iphone 4S 32GB","Iphone 4S 64GB","Iphone 4 8GB","Iphone 4 16GB","Iphone 4 32GB"],

"Samsung||Iphone 5 16GB": ["Select Carrier","ATT","Verizon","Tmobile","Sprint"],

"Samsung||Iphone 5 16GB||ATT": ["Select Condition", "Samsung NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"],

"Samsung||Iphone 5 16GB||Verizon": ["Select Condition", "Samsung NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"],

"Samsung||Iphone 5 16GB||Tmobile": ["Select Condition", "Samsung NewInBox - $350.00","Good - $300.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"],

"Samsung||Iphone 5 16GB||Sprint": ["Select Condition", "Samsung NewInBox - $350.00","Good - $280.00","Cracked - $280.00","Broken - $170.00","Dead - $140.00"]

};



It's technically more correct and saves some typing.

clueful
06-26-2013, 02:21 AM
Using (curly brackets here):


var categories = {};

Not (square brackets):


var categories = [];

Would be more correct and still works. The use of square brackets connotes an array

I doubt that internally it makes any difference.

Nesting the entire declaration inside {}, means that the properties of the object have to be comma-separated, which introduces far more opportunity for error, a major consideration in what may become a published script for use by non-programmers. Conversely declaring each property separately using associative array syntax, in my view makes any error easier to find.

jscheuer1
06-26-2013, 02:43 AM
What you say about the structure makes sense and can be done regardless of whether or not the initial declaration of categories is as an Object or as an Array.

That doesn't change the fact that there are no associative arrays as arrays in javascript. What's known as an associative array in other languages is in javascript an object. For the most part you can probably get away with exploiting the object properties of Array as you have done. But it could get you into trouble at some point. You can have both though, the more verbose, less error prone syntax you say you favor, and proper use of javascript Objects and Arrays:


var categories = {};

categories["Apple"] = ["Select Apple Device", "Iphone 5 16GB","Iphone 5 32GB","Iphone 5 64GB","Iphone 4S 16GB","Iphone 4S 32GB","Iphone 4S 64GB","Iphone 4 8GB","Iphone 4 16GB","Iphone 4 32GB"];

categories["Apple||Iphone 5 16GB"] = ["Select Carrier", "ATT", "Verizon", "Tmobile", "Sprint"];

categories["Apple||Iphone 5 16GB||ATT"] = ["Select Condition", "NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Apple||Iphone 5 16GB||Verizon"] = ["Select Condition","NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Apple||Iphone 5 16GB||Tmobile"] = ["Select Condition", "NewInBox - $350.00","Good - $300.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"];

categories["Apple||Iphone 5 16GB||Sprint"] = ["Select Condition", "NewInBox - $350.00","Good - $280.00","Cracked - $280.00","Broken - $170.00","Dead - $140.00"];


categories["Samsung"] = ["Select Samsung Device", "Iphone 5 16GB","Iphone 5 32GB","Iphone 5 64GB","Iphone 4S 16GB","Iphone 4S 32GB","Iphone 4S 64GB","Iphone 4 8GB","Iphone 4 16GB","Iphone 4 32GB"];

categories[ "Samsung||Iphone 5 16GB" ] = ["Select Carrier","ATT","Verizon","Tmobile","Sprint"];

categories["Samsung||Iphone 5 16GB||ATT"] = ["Select Condition", "Samsung NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Samsung||Iphone 5 16GB||Verizon"] = ["Select Condition", "Samsung NewInBox - $550.00","Good - $400.00","Cracked - $280.00","Broken - $225.00","Dead - $210.00"];

categories["Samsung||Iphone 5 16GB||Tmobile"] = ["Select Condition", "Samsung NewInBox - $350.00","Good - $300.00","Cracked - $280.00","Broken - $150.00","Dead - $120.00"];

categories["Samsung||Iphone 5 16GB||Sprint"] = ["Select Condition", "Samsung NewInBox - $350.00","Good - $280.00","Cracked - $280.00","Broken - $170.00","Dead - $140.00"];



I didn't mean to insist that you adopt the wrapped syntax, merely to suggest it and to point out that it was less verbose.

Something you cannot do though is to use Array as an Object and use the wrapped syntax. So using the technically more correst object gives greater flexibility down the line. Also considering the wide use of jQuery.extend() and similar in other libraries (or even made using native code) when working with Objects, and javascript's own for(var p in Object) where you can use/iterate over both p and Object[p], which can be very handy for working with javascript objects, it's probably best, regardless of the syntax, to use an actual object, rather than an array as an object when an object is what you're constructing. My thinking being that one or more of these methods would not work with a javascript Array used as an Object.