jQuery: Hiding Select Boxes

HI jQuery Experts,

I have a problem with hiding and unhiding the select boxes.

In the code below, by default the first select box is displayed. Selecting any option, will display the next select box, and so on.

I problem comes when I want it such that, if 4 select boxes are displayed and I change the first select box, i need to hide all select boxes after the second one, similarly if I change the second one, the fourth should be removed.

Pls take a look into the entire code:


  <script src="http://code.jquery.com/jquery-latest.js"></script>
<style>
.hidden{display: none;}
</style>
<table border=1>
	<tr>
		<td>
			<select id="aa" class="mySelect">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv1">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="bb" class="mySelect hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv2">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="cc" class="mySelect hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv3">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="dd" class="mySelect hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv4">Test 1</div>
		</td>
	</tr>

</table>

<script>
	$(document).ready(function(){
		
		$('.mySelect').change(function(){
			$(this).nextAll('select:first').removeClass('hidden');
		});


	});	
</script>

The standard technique to achieve this is to hide all of them when any change occurs, and then to only show the ones up to and including the one that was changed.

yes and how do you show all the select boxes thats prior to the ones that changed?

After hiding them you loop through them one by one, possibly by using the .each() method, unhiding them as you go. When you come across the current select field, you can cancel the rest of them by returning false from the function.

Hi Cancer10

This isn’t something that’s too hard to achieve (:

The way it will need to work is:

  • We’ll need to get a reference to all the selects
  • When we have that reference, we’ll be able to figure out what the index was of the one that changed (in the change event handler)
  • Now that we know which one changed, it’s trivial to unhide the next element, and make sure that the elements after the next one remain (or become) hidden

$(document).ready(function(){
    
    // let's get a reference to all the selects.
    // this is actually an array of DOM elements
    var $mySelects = $(".mySelect");
    var idx = 0;

    $('.mySelect').change(function(){

         // find the index of the changed element
         var idx = $mySelects.index(this);

        // now set the next select to visible
        $mySelects.eq(idx+1).removeClass("hidden");

        // make sure that all selects that come *after* the next element are hidden
        $(".mySelect:gt("+(idx+1)+")").addClass("hidden");
    });

 });

Thank you AussieJohn,

Your solution works like a charm.

I have a question just to make myself clear.

What does the $ symbol do in var $mySelects

Thanks

Whenever I assign a jQuery object to a variable, I tend to prefix that variable with a $ - it doesn’t do anything extra, but it’s kind of a personal code convention so that I can easily identify variables that are cached jQuery objects in my code.

Thanks for explaining that.

Now, that I understood the basics of this, I ahve a more complex structure. Where I have 3 sets of select boxes with 4 select box in each set. With select class named as “mySelectA”, “mySelectB”, “mySelectC”,

These sets are independednt of each other. The concept I mentioned in my first post applies to all of these but these sets should work independly. But I need to pull all the code under a single CHANGE event.

Whats the most efficient code to make this happen without much duplication of variables or codes. Or is it not possible at all?


<table border=1>
	<tr>
		<td>
			<select id="aa" class="mySelectA">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv1">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="bb" class="mySelectA hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv2">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="cc" class="mySelectA hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv3">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="dd" class="mySelectA hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv4">Test 1</div>
		</td>
	</tr>

------------
	<tr>
		<td>
			<select id="ee" class="mySelectB">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv1">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="ff" class="mySelectB hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv2">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="gg" class="mySelectB hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv3">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="hh" class="mySelectB hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv4">Test 1</div>
		</td>
	</tr>
---
	<tr>
		<td>
			<select id="ii" class="mySelectC">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv1">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="jj" class="mySelectC hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv2">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="kk" class="mySelectC hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv3">Test 1</div>
		</td>
	</tr>
	<tr>
		<td>
			<select id="ll" class="mySelectC hidden">
				<option>123</option>
				<option>456</option>
			</select>
			<div class="dv4">Test 1</div>
		</td>
	</tr>


</table>


Thanks for all your help.

Yeah, this is definitely within the realms of the achievable, it is a little bit more complicated though.

Firstly, I’d suggest some changes to the markup, so it will be a bit easier to work this out. Essentially what we want is to get groups of select boxes. So, just staying with your currently table layout, I’ve create 3 tables, each with a class of “mySelectGroup” so we can identify them.


<table border="1" class="mySelectGroup">
    <tr>
        <td>
            <select id="aa" class="mySelect"> 
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv1">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="bb" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv2">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="cc" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv3">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="dd" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv4">Test 1</div>
        </td>
    </tr>
</table>
 
<hr>

<table border="1" class="mySelectGroup">
    <tr>
        <td>
            <select id="ee" class="mySelect">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv1">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="ff" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv2">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="gg" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv3">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="hh" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv4">Test 1</div>
        </td>
    </tr>
 </table>

 <hr>

<table border="1" class="mySelectGroup">
    <tr>
        <td>
            <select id="ii" class="mySelect">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv1">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="jj" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv2">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="kk" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv3">Test 1</div>
        </td>
    </tr>
    <tr>
        <td>
            <select id="ll" class="mySelect hidden">
                <option>123</option>
                <option>456</option>
            </select>
            <div class="dv4">Test 1</div>
        </td>
    </tr>
</table>

Secondly, we’ll have to treat the way the event handling is done a bit differently.

  • We’ll bind an event handler on the groups and watch for changes on the select.
  • Then, once a select has changed, we can find out what group it belongs to, and get all the selects in that group
  • From here, it’s pretty much the same as before, find the index of the changed select and hide/show other selects in the group accordingly
  • Extra bonus: I’ve added in some extra code to cache the selects that belong to a group so that jQuery doesn’t need to find those selects every time one changes. It does this by storing the selects that it found on the mySelectGroup that it belongs to.

$(document).ready(function(){
    
    // We'll bind an event handler on the selects in each group
    // This uses jQuery 1.7's new .on() method
    $('.mySelectGroup').on("change", "select", function(e){
        
        var $group, $mySelects;
        
        //find our current group
        $group = $(this).closest(".mySelectGroup");
        
        //let's cache our set of selects at first use
        if ( typeof $group.data("mySelects") ===  'undefined' ){

            //find all the selects for the current group
            $mySelects = $group.find(".mySelect");

            //assign the selects to a data object stored on the group for later use
            $group.data("mySelects", $mySelects)
            
        }
        else {
            //we can use the cached data.
            $mySelects = $group.data("mySelects");
        }
        
        // find the index of the changed element
        idx = $mySelects.index(this);

        // now set the next select to visible
        $mySelects.eq(idx+1).removeClass("hidden");

        // make sure that all selects that come *after* the next element are hidden
        //
        // we're now using .slice() rather than :gt() as we're no longer selecting from a parent, 
        // but rather getting a "slice" of an array. This also means we're slicing the index + 2 
        // because the next element (from the index) needs to remain visible
        $mySelects.slice(idx+2).addClass("hidden");
    });

});    

See:

Many thanks sir.

I will give this a try and shall come back to you if i face any problem.