views:

261

answers:

2

This is a little more complicated than the title makes it out to be, but here are the essential business rules:

  1. There are three select menus on the page, each filled with the same options and values.
  2. There will always be three select menus.
  3. There will always be the same number of options/values in each select menu.
  4. Selecting a question in any of the menus will remove that question as an option from the other two menus.
  5. Re-selecting a different question from any of the menus will bring back the question that was previously removed from the other two menus at the index it was at previously.

I've tried this a few different ways, and the thing that is killing me is number 5. I know that it wouldn't be inserted at the exact index because some questions may have already been removed, which would reorder the index. It basically needs an insertBefore or insertAfter that puts it in the same "slot".

Even if you don't post any code, some thoughts on how you might approach this would be extremely helpful. The select menus and jQuery look something like this, but I've had numerous tries at it in different variations:

jQuery:

$(function() {
    $(".questions").change(function() {
        var t = this;
        var s = $(t).find(":selected");

        // Remove, but no "insert previously selected" yet...

        $(".questions").each(function(i) {
            if (t != this) {
                $(this).find("option[value=" + s.val() + "]").remove();
            }
        });
    });
});

HTML:

<select name="select1" class="questions">
    <option value="1">Please select an option...</option>
    <option value="2">What is your favorite color?</option>
    <option value="3">What is your pet's name?</option>
    <option value="4">How old are you?</option>
</select>
<select name="select2" class="questions">
    <option value="1">Please select an option...</option>
    <option value="2">What is your favorite color?</option>
    <option value="3">What is your pet's name?</option>
    <option value="4">How old are you?</option>
</select>
<select name="select3" class="questions">
    <option value="1">Please select an option...</option>
    <option value="2">What is your favorite color?</option>
    <option value="3">What is your pet's name?</option>
    <option value="4">How old are you?</option>
</select>
+5  A: 

Don't remove the elements, hide them. With removing, you are causing you a lot more problems than necessary. This works for me:

$(function() {
    $('select.questions').change(function() {            
        var hidden = [];
        // Get the values that should be hidden
        $('select.questions').each(function() {
            var val = $(this).find('option:selected').val();
            if(val > 0) {
                hidden.push($(this).find('option:selected').val());
            }
        });
        // Show all options...          
        $('select.questions option').show().removeAttr('disabled');            
        // ...and hide those that should be invisible
        for(var i in hidden) {
            // Note the not(':selected'); we don't want to hide the option from where
            // it's active. The hidden option should also be disabled to prevent it
            // from submitting accidentally (just in case).
            $('select.questions option[value='+hidden[i]+']')
                .not(':selected')
                .hide()
                .attr('disabled', 'disabled');
        }
    });
});

I made a small change to your HTML also, I denoted an option that should always be visible with a value of 0. So the valid options go from 1 to 3.

Here's a working example, tell me if I misunderstood you:

http://www.ulmanen.fi/stuff/selecthide.php

Tatu Ulmanen
Damn you beat me ;-) I second Tatu's method.
Tom Bartel
I haven't checked cross-browser yet, but I thought that show/hide was reserved for select elements, and could not be applied to their individual options?
hal10001
+1, although on IE it will never hide the options (they just get disabled).
Lance McNearney
Hmm, well, I guess the disabled state is an acceptable fallback solution then. I just blindly assumed it works on all browsers as it worked with FF :)
Tatu Ulmanen
This actually does not work on IE7/WinXP. We had a client test, and it does not work, and I tested and it does not work. Our solution was to rebuild the list of values each time, and then just remove what wasn't needed. I've talked to several other developers, and this seems to be the most full proof is that you always rebuild the option list.
hal10001
A: 

I was working on a solution of this recently and modified this code to remove rather than disable/hide. For my solution it was required (I'm also using UI to style the select elements). Here's how I did it:

// Copy an existing select element
var cloned = $('select[name="select1"]').clone();

// Each time someone changes a select
$('select.questions').live('change',function() {
    // Get the current values, then reset the selects to their original state
    var hidden[];
    $('select.questions').each(function() {
        hidden.push($(this).val());
        $(this).html(cloned.html());
    });
    // Look through the selects
    for (var i in hidden) {
        $('select.questions').each(function() {
            // If this is not the current select
            if ((parseInt(i)) != $(this).parent().index()) {
                // Remove the ones that were selected elsewhere
                $(this).find('option[value="'+hidden[i]+'"]').not('option[value="0"]').remove();
            } else {
                // Otherwise, just select the right one
                $(this).find('option[value="'+hidden[i]+'"]').not('option[value="0"]').attr('selected','selected');
            }
        });
    }
});
Marcus