Link to home
Start Free TrialLog in
Avatar of khziar
khziar

asked on

Filtering multiple combo items without reloading

There are 3 combo boxes with first box filled with a few values. The list items in 2nd combo depend on the selection of 1st combo, and same with the 3rd one. I want to fetch all the data during the page load time once and then fill the boxes accordingly.  Key is to avoid page reloading on each combo selection once page is there. I need help.
Avatar of hongjun
hongjun
Flag of Singapore image

Avatar of khziar
khziar

ASKER

ya, its quite close, but can i see any of the given demo working. I have tried 4 but they are not seems working.
ASKER CERTIFIED SOLUTION
Avatar of icegroup
icegroup

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of fritz_the_blank
This is how I do it with two--but you can extend the logic to make it work with three:

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE></TITLE>
<SCRIPT LANGUAGE=javascript>
<!--

// Populate an array with all of the models from the different
// manufactures in the order that the manufacturers appear in the
// drop-down list. In this example, I'll use Ford and Toyota
// of course, you'll want to generate these via server-side vbscript

models = new Array(3)
models [0] = new Array(1)
models [1]= new Array(3)
models [2] = new Array(4)

//Empty (0)
models [0][0.0] = " "
models [0][0.1] = " "

//Ford  (3)
models [1] [0.0] = "http://www.Ford.com/Tempo"
models [1] [0.1] = "Tempo"
models [1] [1.0] = "http://www.Ford.com/Tauras"
models [1] [1.1] = "Tauras"
models [1] [2.0] = "http://www.Ford.com/Windstar"
models [1] [2.1] = "Windstar"

//Toyota   (2)
models [2][0.0] = "http://www.Toyota.com/Tercel"
models [2][0.1] = "Tercel"
models [2][1.0] = "http://www.Toyota.com/Corolla"
models [2][1.1] = "Corolla"
models [2][2.0] = "http://www.Toyota.com/Camry"
models [2][2.1] = "Camry"
models [2][3.0] = "http://www.Toyota.com/Avalon"
models [2][3.1] = "Avalon"


//============================================

//Next, we create a function to fill the second drop down from
//the array based on the item selected in the first drop down.

function FillList()
{
var num=document.formname.manufacturer.selectedIndex
var boxlength = 0

document.formname.models.selectedIndex = 0
for ( ctr=0;ctr<models[num].length;ctr++)
 {
 boxlength++;
 document.formname.models.options[ctr] = new Option(models[num] [eval(ctr + ".1")], models[num][eval(ctr + ".0")]);
 }

document.formname.models.length = boxlength;
document.formname.models.options.length = boxlength;
document.formname.models.focus() ;

}


//-->
</SCRIPT>
</HEAD>
<BODY>
<FORM action="" method=post id=form1 name=formname>
<SELECT id=select1 name=manufacturer onChange="JavaScript:FillList()">
<OPTION value=""></OPTION>
<OPTION value=Ford >Ford</OPTION>
<OPTION value=Toyota>Toyota</OPTION>

</SELECT>

<p>
<SELECT id=select2 name=models onChange="javascript:location.replace(this.value)">
<OPTION></OPTION>
</SELECT>

</FORM>
</BODY>
</HTML>

Fritz the Blank
a brief explanation ...

i use an associate array - when you select an option, i grab the selected index and get the "value" associated with the selected option.

the value is something like "A1" or "B3" etc. This is just a key into the global hash. You can use any string as a key - so i believe you just need to decide on something unique for each selection to allow you to "lookup" the array and select box to populate.

then, simply populate your js arrays - you can even use multidimensional arrays - i just didn't want to confuse you ... you can even use the index as a direct key into an array and skip the "A1" nomenclature altogether ...

does that make sense ?
icegroup,

I really like your code! It is too bad that try/catch doesn't work with Netscape 4.x


Fritz the Blank
Hi Fritz,

Thanks. The idea behind both our snippets is exactly the same. try catch really really comes in handy for little bits like that - coding the common case first and throwing the other browser in the catch block.

Many purists would disagree and say thats not how to properly use try catch - but it sure does serve an excellent purpose here and I think the code is so fast that its fine. There's no guessing or checking string length or funky if statements to determine browser type and its cleaner.

Basically - try this first - if it doesn't work, try that but don't complain!

But you are sooo right. This only works on the most recent browsers. Obviously - you could write proper code to test for browser "model" before enacting code. It depends on the user's target audience - but at least we've given him a start and I think thats the whole point. ;)
Try this code. Make it an include on your page:

********************************************************

 handle_events_on_filter = false;
      refill_original_value_on_filter = false;
      
      // Gets the value of a select
      function getValue(SelectToGet)
            {
            // Create array to return.
            var SelectedArray = new Array();
            
            //loop through options, put selected into an array and return      
            for(var optionIndex=0; optionIndex < SelectToGet.options.length; optionIndex++)
                  if (SelectToGet.options[optionIndex].selected)
                        SelectedArray[SelectedArray.length] = SelectToGet.options[optionIndex].value
            
            return SelectedArray;
            }
      
      // Gets the value of a select
      function getSelected(SelectToGet)
            {
            return (SelectToGet.selectedIndex >= 0) ? SelectToGet.options[SelectToGet.selectedIndex] : false;
            }
            
      // Creates an array in the format SelectName_Array
      function createArray(SelectToArray)
            {
            ArrayName = SelectToArray.name + "_Array"
            eval(ArrayName + " = new Array()");
            setArray(SelectToArray)
            }
      
      // Fill associated array with elements from select
      // Each element of array is an array in format [filter, text, value]
      function setArray(SelectToSet)
            {
            var SelectArray = eval(SelectToSet.name + "_Array")
            var SetArrayStart = ((SelectToSet.options.length) && (SelectToSet.options[0].value == "")) ? 1:0;
            for(var optionIndex=SetArrayStart; optionIndex<SelectToSet.options.length; optionIndex++)
                  with(SelectToSet.options[optionIndex])
                        SelectArray[SelectArray.length] = [ filter, text, value ];
            }
      
      // Set each option other than first to null to release, then set count to one
      function clearSelect(SelectToClear)
            {
            with(SelectToClear)
                  {
                  var ClearStart = ((options.length) && (options[0].value == "")) ? 1:0;
                  for(var optionIndex=ClearStart; optionIndex<options.length; optionIndex++)
                        options[optionIndex] = null;
                  // release memory, but leave first option.
                  SelectToClear.length = ClearStart;
                  }
            }
      
      // create new instance of option, and set filter var
      function addOption(SelectToFilter,filter,text,value)
            {
            SelectToFilter.options[SelectToFilter.options.length] = new Option(text,value);
            SelectToFilter.options[SelectToFilter.options.length-1].filter = filter;
            }
      
      // Returns true if the element is found in the array, false otherwise
      function IsInArray(SearchArray,SearchValue)
            {
            if ( typeof(SearchArray) == 'string' )
                  {
                  // Convert to an array
                  if ( SearchArray.indexOf(",") >= 0 )
                        { SearchArray = SearchArray.split(","); }
                  // Compare strings
                  else { return (SearchArray == SearchValue); }
                  }
            
            if ( typeof(SearchArray) != 'undefined' )
                  {
                  // Flip through array, and return true is value found
                  for(var arrayIndex=0; arrayIndex<SearchArray.length; arrayIndex++)
                        if(SearchArray[arrayIndex] == SearchValue)
                              { return true; }
                  }
                                                
            return false;
            }
      
      // Clear select, filter array, then insert appropriate options      
      function filterSelect(SelectToFilter,FilterFromSelect)
            {
            // Get the filter value, and determine if you should refill the entire select
            if (typeof(FilterFromSelect) == 'object')
                  {
                  var filter = getValue(FilterFromSelect);
                  var RefillAll = ((filter == "") && (FilterFromSelect.selectedIndex <= 1));
                  }
            else
                  {
                  var filter = FilterFromSelect;
                  var RefillAll = (filter == "");
                  }
            
            // After we filter the select we'll look for this value and set it selected again
            var OriginalValue = (getSelected(SelectToFilter)) ? getSelected(SelectToFilter).value : "";
            
            // Check if an associated array already exists for select, if not create one
            ArrayString = "typeof(" + SelectToFilter.name + "_Array)"
            if (eval(ArrayString) == 'undefined')
                  createArray(SelectToFilter);
            
            // Clear the current values of the select      
            clearSelect(SelectToFilter);
            
            // Refill the select from the associated array if the filter criteria match
            for(var arrayIndex=0; arrayIndex < eval(SelectToFilter.name + "_Array.length"); arrayIndex++)
                  {
                  if(( IsInArray(filter, eval(SelectToFilter.name + "_Array[" + arrayIndex + "][0]")) ) || (RefillAll))
                        {
                        text = eval(SelectToFilter.name + "_Array[" + arrayIndex + "][1]");
                        value = eval(SelectToFilter.name + "_Array[" + arrayIndex + "][2]");
                        addOption(SelectToFilter,filter,text,value);
                        }
                  }
            
            // Check sibling for an onchange function, and call it if it's not null
            if(RefillAll)
                  if(typeof(SelectToFilter.onchange) != 'undefined')
                        if (SelectToFilter.onchange != null)
                              { SelectToFilter.onchange();}
            
            // If we didn't refill everything, look for the previously selected value
            if ( refill_original_value_on_filter && (OriginalValue != "") && !(RefillAll) )
                  {
                  for (var optionIndex=0; optionIndex<SelectToFilter.options.length; optionIndex++)
                        {
                        if ( SelectToFilter.options[optionIndex].value == OriginalValue )
                              { SelectToFilter.options[optionIndex].selected = true; break; }
                        }
                  }
            
            if (handle_events_on_filter)
                  {
                  // Handle the possible events for changing
                  if ( (typeof(SelectToFilter.onfocus) != 'undefined') && (SelectToFilter.onfocus != null) ) { SelectToFilter.onfocus(); }
                  if ( (typeof(SelectToFilter.onclick) != 'undefined') && (SelectToFilter.onclick != null) ) { SelectToFilter.onclick(); }
                  if ( (typeof(SelectToFilter.onchange) != 'undefined') && (SelectToFilter.onchange != null) ) { SelectToFilter.onchange();}
                  if ( (typeof(SelectToFilter.onblur) != 'undefined') && (SelectToFilter.onblur != null) ) { SelectToFilter.onblur(); }
                  }
            
            return true;
            }

********************************************************
Now, here's how to implement the code:

Let's say you've got a select like this:

<form name="carForm">

<select name="company" onchange="filterSelect(model,this)">
  <option value="ford">Ford</option>
  <option value="chevy">Chevy</option>
  <option value="audi">Audi</option>
</select>

<select name="model" onchange="filterSelect(edition,this)">
  <option value="pickup" filter="ford">Ford Pickup</option>
  <option value="caprice" filter="chevy">Caprice</option>
  <option value="A6" filter="audi">A6</option></select>

<select name="edition">
  <option value="pickup">F150</option>
  <option value="pickup">F250</option>
  <option value="pickup">F350</option>
  <option value="caprice">Caprice Classic</option>
  <option value="pickup">Caprice Wagon</option>
  <option value="pickup">A6 2.7L</option>
  <option value="pickup">A6 4.2L</option>
</select>
</form>
**************************************************

Because the code dynamically creates the array that stores the values, you don't have to list all of the data twice on your page. This decreases the download time.

Now the filter value on the option is the value that will be filtered upon. So as you can see in the model example, the onchange function says "filter the model selct based on the value of this select". It will go in and remove any options that don't match the value of the model select.

*****************************************************

Cool things built in:
If you select an option with no value, it will refill the next option. IE, if the first option of model was blank,a nd the user selects it, it will refill all of the selects below it with the original option set.

handle_events_on_filter = false;
refill_original_value_on_filter = false;

These 2 parameters will allow you change what happens when the selects are filtered. The first "handle_events_on_filter", when set to true, will execute the onchange and other events for the select your filtering. So when you select the company, it filters the model. And if this flag is set to true, it will then handle the onchange events for the model select. This allows sortof event bubbling.

refill_original_value_on_filter is a little trickier to explain. Basically, if the user had selected "caprice" first, THEN selected chevy, this will attempt to keep caprice selected. It works depending on how you have your selects setup.

********************************************************

I have a newer version of this code that I will post later that will also allow you to enable and disable the subsequent select. Such that you cannot select the model until you have selected the make. I'll post this soon I hope, I'm not at home right now though.

If you decide you like this code, the other code will work right ontop of it.

If you have trouble configuring anything (should be rather simple, just include the page and put the appropriate onchange event in your selects) let me know and I can help you out.

-Mike
Try this SELECT form instead, it may make more sense to you:

********************************************************

<form name="carForm">

<select name="company" onchange="filterSelect(model,this)">
 <option value=""></option>
 <option value="ford">Ford</option>
 <option value="chevy">Chevy</option>
 <option value="audi">Audi</option>
</select>

<select name="model" onchange="filterSelect(edition,this)">
 <option value="" filter=""></option>
 <option value="pickup" filter="ford">Ford Pickup</option>
 <option value="van" filter="ford">Vans</option>
 <option value="caprice" filter="chevy">Caprice</option>
 <option value="corvette" filter="chevy">Corvette</option>
 <option value="A4" filter="audi">A4</option>
 <option value="A6" filter="audi">A6</option>
</select>

<select name="edition">
 <option value="" filter=""></option>
 <option value="F150" filter="pickup">F150</option>
 <option value="F250" filter="pickup">F250</option>
 <option value="F350" filter="pickup">F350</option>
 <option value="CClassic" filter="caprice">Caprice Classic</option>
 <option value="COnv" filter="corvette">Corvette Convertible</option>
 <option value="CZ6" filter="corvette">Corvette Z6</option>
 <option value="Wagon" filter="caprice">Caprice Wagon</option>
 <option value="Cvan" filter="van">Cargo Van</option>
 <option value="A627" filter="A6">A6 2.7L</option>
 <option value="A643" filter="A6">A6 4.3L</option>
 <option value="A427" filter="A4">A4 2.7L</option>
 <option value="A430" filter="A4">A4 3.0L</option>
</select>
</form>
that filter is sweet.
This is a very interesting variant on a long-standing problem. I would like to play with it a bit to see how it works with different browsers and etc.

What I like in particular is how easy it will be to create the selects by iterating through a database. With the code that I posted, I generate the arrays by using Response.Write to sketch out the arrays in the html code. It works, but it is a wee bit cumbersome. It seems that this last example would be easy to implement.

Fritz the Blank
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
khziar,
No comment has been added lately (423 days), so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area for this question:

RECOMMENDATION: split points between icegroup http:#7428308 and TallerMike http:#7429207

Please leave any comments here within 7 days.

-- Please DO NOT accept this comment as an answer ! --

Thanks,

mplungjan
EE Cleanup Volunteer