jQuery - Removing Options from Two Related Selects
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>
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();
