jQuery - Removing Options from Two Related Selects
Posted on Nov 18, 2009
Here's the situation: you have form with two selects containing the
same options, for example, to choose your favorite sports team and a
secondary favorite. In this scenario, you'd want to ensure that your
user doesn't choose the same option in both select boxes. You could
accomplish this simply via server-side validation but that wouldn't be
very user-friendly. Instead, we can use a little jQuery magic to remove
the invalid option from the other select list. In order to accomplish
this task, we'll utilize some jQuery selectors and traversal methods as
well as the data() method (you can see the code in action here).
I have to admit that this task was,
for me, much trickier than it sounds. In the end I actually went
through several iterations of the method to get this to work right. My
first version actually relied on the fact that, in my scenario, the
select option values were all numeric but I wanted something more
generic that could work for any scenario. After
the fact, some people directed me to the Select
Plugin but it doesn't seem to necessarily solve this
admittedly niche issue.
The method I created (see code
below) expects you to tell it which is the primary select box (i.e. the
one you just changed) and the secondary select box (i.e. the one you
want to remove the option from). We also utilize a hidden select box
which functions simply as a holder of the removed options. I have
hardcoded an ID in this method but you could modify this to make it
more generic should you wish. I also assume there is an option with a
value of 0 which is your "select one" option.
Let me give a
brief overview of what I am trying to do here without going line by
line (I attempted a line by line explanation but it was confusing).
Essentially, the steps are:
- Determine the selected option in the primary select (selectA) and secondary select (selectB);
- Get the item that is not one of the selected values back from the hidden select box and put it back, being sure to place it following the preceding ID value which was appended to it via the data() method in jQuery;
- If you didn't choose the "select one" value (i.e. 0) then remove the option from selectB that coincides with the selected option in selectA and place it in the hidden select being sure to append the preceding option's ID value via the data() method (NOTE: after thinking about this, I could simply have added the items current index in the options as data which might be clearer but would function identically);
- Reselect the selected value in selectB since it seems to get deselected with all this adding and removing options.
I am fairly certain that someone will offer me a better solution for this - there always seems to be one (and it seems like every jQuery post gets dissected line by line on Reddit). However, feel free to borrow the code if this is solves a need in your application.
<html>
<head>
<title>jQuery Examples</title>
<script src="scripts/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
function removeOptions(selectA,selectB) {
var selectedValue = $(selectA).children(":selected").attr("value");
var otherValue = $(selectB).children(":selected").attr("value");
// get the other element from the hidden select to put back
var prior = $("#hiddenContainer").children("[value!="+otherValue+"]").data("prior");
if (prior != undefined) {
$("#hiddenContainer").children("[value!=" + otherValue + "]").insertAfter($(selectB).children("[value=" + prior.prior + "]"));
}
if (selectedValue != 0) {
// add the prior id data to the element before removing it
var priorValue = $(selectB).children("[value="+selectedValue+"]").prev().attr("value");
$(selectB).children("[value="+selectedValue+"]").data("prior",{prior:priorValue});
// move the selected element of selectA in selectB to hidden select
$(selectB).children("[value="+selectedValue+"]").appendTo("#hiddenContainer");
}
// reselect the option in the secondary select
$(selectB).val(otherValue);
}
</script>
</head>
<body>
<select name="optionsA" id="optionsA" onchange="removeOptions($(this),$('#optionsB'))">
<option value="0">Select One</option>
<option value="1">Option 1</option>
<option value="two">Option 2</option>
<option value="3">Option 3</option>
<option value="4">Option 4</option>
<option value="5">Option 5</option>
<option value="6">Option 6</option>
</select>
<br />
<select name="optionsB" id="optionsB" onchange="removeOptions($(this),$('#optionsA'))">
<option value="0">Select One</option>
<option value="1">Option 1</option>
<option value="two">Option 2</option>
<option value="3">Option 3</option>
<option value="4">Option 4</option>
<option value="5">Option 5</option>
<option value="6">Option 6</option>
</select>
<select name="hiddenContainer" id="hiddenContainer" style="visibility:hidden;">
</select>
</body>
</html>
Comments
As an alternative, you could hide() the duplicate option or simply disable it, eliminating the need to move around DOM elements. The only issue with that approach is that if the option you want to hide/disable is already selected in that drop-down, you'd need to reset the value of the drop-down to the "Select One" option.
Posted By Brian Swartzfager / Posted on 11/19/2009 at 7:39 AM
One of my few disappointments with jQuery is working with select lists. Having to treat the options distinctly from the select always makes me think twice.
This plug-in makes it a bit easier to manipulate selects: http://www.texotela.co.uk/code/jquery/select/
$(selectA).children(":selected").attr("value");
turns into
$(selectA).selectedValue();
Posted By drew / Posted on 11/19/2009 at 12:58 PM
Hi,
I tried to implement the same for 3 dropdowns, but somehow it is not functioning properly.
Please let me know whether I can scale this to as many dropdown as needed.
Here is the piece of code:
<script type="text/javascript">
function removeOptions(selectA,selectB,selectC) {
var selectedValue = $(selectA).children(":selected").attr("value");
var otherValue = $(selectB).children(":selected").attr("value");
var otherValue1 = $(selectC).children(":selected").attr("value");
// get the other element from the hidden select to put back
var prior = $("#hiddenContainer").children("[value!=" + otherValue + "]").data("prior");
var prior1 = $("#hiddenContainer1").children("[value!=" + otherValue1 + "]").data("prior1");
if (prior != undefined) {
$("#hiddenContainer").children("[value!=" + otherValue + "]").insertAfter($(selectB).children("[value=" + prior.prior + "]"));
}
if (prior1 != undefined) {
$("#hiddenContainer1").children("[value!=" + otherValue1 + "]").insertAfter($(selectC).children("[value=" + prior1.prior1 + "]"));
}
if (selectedValue != 0) {
// add the prior id data to the element before removing it
var priorValue = $(selectB).children("[value=" + selectedValue + "]").prev().attr("value");
$(selectB).children("[value=" + selectedValue + "]").data("prior", { prior: priorValue });
var priorValue1 = $(selectC).children("[value=" + selectedValue + "]").prev().attr("value");
$(selectC).children("[value=" + selectedValue + "]").data("prior1", { prior1: priorValue1 });
// move the selected element of selectA in selectB to hidden select
$(selectB).children("[value=" + selectedValue + "]").appendTo("#hiddenContainer");
$(selectC).children("[value=" + selectedValue + "]").appendTo("#hiddenContainer1");
}
// reselect the option in the secondary select
$(selectB).val(otherValue);
$(selectC).val(otherValue1);
}
</script>
<select name="hiddenContainer" id="hiddenContainer" style="visibility:hidden;">
</select>
<select name="hiddenContainer1" id="hiddenContainer1" style="visibility:hidden;">
</select>
Your inputs are greatly appreciated, thanks!!
Posted By shamanth / Posted on 08/31/2010 at 1:40 PM