views:

50

answers:

3

I'm trying to set up a form with multiple dropdown lists, that will allow visitors to rank the list items. Sample form:

<form action="" name="rankem">
  <select name="rank1" id="rank1">
    <option value="option1">Option 1</option>
    <option value="option2">Option 2</option>
    <option value="option3">Option 3</option>
    <option value="option4">Option 4</option>
    <option value="option5">Option 5</option>
  </select>
  <select name="rank2" id="rank2">
    <option value="option1">Option 1</option>
    <option value="option2">Option 2</option>
    <option value="option3">Option 3</option>
    <option value="option4">Option 4</option>
    <option value="option5">Option 5</option>
  </select>
  <select name="rank3" id="rank3">
    <option value="option1">Option 1</option>
    <option value="option2">Option 2</option>
    <option value="option3">Option 3</option>
    <option value="option4">Option 4</option>
    <option value="option5">Option 5</option>
  </select>
</form>

As options are selected, I'd like to remove (detach) them from each dropdown.

The actual lists I have contain a lot more options so that people can rank the top 10 out of 32 options, so imagine the sample code above with 10 dropdowns with 32 options each.

If somebody selects 'option2' from select#rank1, I'd like option2 removed from subsequent dropdowns, and so on. If they go back to select#rank1, and change their selection to 'option4', 'option2' will need to be appended to all the dropdowns.

I've taken a look at .detach() and .append() but don't really know what I'm doing.

$("option[value="+$(this).val()+"]").detach();

I tried the above to start but it just removes the option from all dropdowns. I'm a JS/jquery noob so any help or links in the right direction is appreciated. Thanks!

+1  A: 

Try this line instead (for detaching the elements):

$("select:not(#"+$(this).parent().attr('id')+") option[value="+$(this).val()+"]").detach();
You
Tried that out but no go. Still works the same with the option getting removed from all lists. Don't know if this helps but I added -- alert($(this).parent().id); -- and it pops up undefined.
sonofrodrigo
Fixed, should work now.
You
And thanks to You for that too. Sadly, I can't vote up yet...
sonofrodrigo
A: 

Have you considered enabling/disabling vs. attaching and detaching?

I'm not as quick with the code (working with a broken arm here), but the general algorithm would be this:

$(function() {
    // Respond to any value change.
    $('select').change(function() {
        var val = $(this).val();

        // Enable/disable depending on the selection.
        // Removed the each loop after seeing You's answer (+1). He has a better solution.
        $('select:not(#' + $(this).parent().id + ') option[value=' + val + ']').removeAttr('disabled');
        $('select(#' + $(this).parent().id + ') option[value=' + val + ']').attr('disabled', 'disabled');
    });
});

This has not been tested and I'm still very much learning jQuery (so I'll take feedback).

JasCav
That works great. Thanks! Now I just need to add some code to re-enable the option if it gets deselected later if the user changes their mind and picks something else. I think I've got enough to run with, but I'll be back if I can't sort it out... Thanks again!
sonofrodrigo
A: 

This will detach the option from each of the selects which has a rank number larger than the current select and will put it into a javascript object where the key is the parent select's id. Each time a select is changed, only its subsequent selects will be affected, not the ones higher in the chain. So if you select from rank2, it will be detached from rank3, but not from rank1. Reading your question again, I'm not certain that this is the funcationality you're looking for, but here it is anyway.

function rankIndex(id){
  return id.substr(4);
}

$(document).ready(function(){

  var removed = {};
  $('select').each(function(){
    // define each select's bin for removed elements as an object
    removed[$(this).attr('id')] = {};
  });

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

    var $this = $(this),
        $thisId = $this.attr('id'),
        $thisRankIndex = rankIndex($thisId);

      // add each element back to selects further in the chain
    $.each(removed[$thisId], function(selectId, option){
      $('#'+selectId).append(option);
    });

    // filter the selects that are further in the chain 
    // and detach the options into the removed bin
    $('select').filter(function(i){
      return rankIndex($(this).attr('id')) > $thisRankIndex;
    }).find('option[value='+$this.val()+']').each(function(){
      removed[$thisId][$(this).closest('select').attr('id')] = $(this).detach();
    });

  });

});

Some key things to read up on to further understand what I did are the javascript object and some jQuery functions: filter(), each() (as well as jQuery.each()) and find(). Anyone, if you can, feel free to refactor this as it's a bit verbose. Hope this helps!

munch
Thanks for that but, yeah, I'd need to remove the option from all dropdowns -- including ones higher in the chain.
sonofrodrigo