Move options between Select Menus with Javascript

Posted on June 6, 2008

I’ve done this countless times, yet I can never seem to remember how I did it the next time I need the code. This is so I don’t have to hit up Google next time.

Move a single option between menus

So here’s the basic form:

Form with two select menus

The code is simple enough.

Form code

<form id="foo" name="foo" action="" onsubmit="return false;">
<table>
  <tr>
    <td>
      <select id="LEFT_MENU" size="10">
        <option value="1">One</option>
        <option value="2">Two</option>
        <option value="3">Three</option>
        <option value="4">Four</option>
        <option value="5">Five</option>
        <option value="6">Six</option>
        <option value="7">Seven</option>
        <option value="8">Eight</option>
        <option value="9">Nine</option>
        <option value="10">Ten</option>
      </select>
    </td>
    <td valign="middle">
      <p><input type="button" id="moveRight" value="&gt;" 
        onclick="moveOption('LEFT_MENU','RIGHT_MENU')"></p>
      <p><input type="button" id="moveLeft" value="&lt;" 
        onclick="moveOption('RIGHT_MENU','LEFT_MENU')"></p>
    </td>
    <td>
      <select id="RIGHT_MENU" size="10"></select>
    </td>
  </tr>
</table>
</form>

Rather than have two functions moveLeft() and moveRight(), I’m going to go with a single function that uses the ID of both select menus to swap an option from one to the other.

Javascript function to swap select menu options

<script type="text/javascript">
function moveOption( fromID, toID ) {
  var i = document.getElementById( fromID ).selectedIndex;
  var o = document.getElementById( fromID ).options[ i ];
  var theOpt = new Option( o.text, o.value, false, false );

  document.getElementById( toID )
    .options[document.getElementById( toID ).options.length] = theOpt;
  document.getElementById( fromID ).options[ i ] = null;
}
</script>

Let’s go through this line by line:

Get the index of the option selected in the left menu.

var i = document.getElementById( fromID ).selectedIndex;

Get the option object associated to the index i in the left menu.

var o = document.getElementById( fromID ).options[ i ];

Create a new option object based on the select option.

/*
The Option object has 4 properties
Option( text, value, defaultSelected, selected )
*/
var theOpt = new Option( o.text, o.value, false, false );

Put the new option object into the array of options in the right menu.

document.getElementById( toID ).options[document.getElementById( toID ).options.length] = theOpt;

Remove the select option from the left menu.

document.getElementById( fromID ).options[ i ] = null;

Moving multiple options between menus

Here’s the same form where we can select more than one option at a time.

Form with two multiple select menus

The HTML code has a slight change to allow for multiple options to be selected.

Form code

<form id="foo2" name="foo2" action="" onsubmit="return false;">
<table>
  <tr>
    <td>
      <select id="LEFT_MENU2"  multiple="true" size="10">
        <option value="1">One</option>
        <option value="2">Two</option>
        <option value="3">Three</option>
        <option value="4">Four</option>
        <option value="5">Five</option>
        <option value="6">Six</option>
        <option value="7">Seven</option>
        <option value="8">Eight</option>
        <option value="9">Nine</option>
        <option value="10">Ten</option>
      </select>
    </td>
    <td valign="middle">
      <p><input type="button" id="moveRight2" value="&gt;"
        onclick="moveOptions('LEFT_MENU2','RIGHT_MENU2')"></p>
      <p><input type="button" id="moveLeft2" value="&lt;"
        onclick="moveOptions('RIGHT_MENU2','LEFT_MENU2')"></p>
    </td>
    <td>
      <select id="RIGHT_MENU2"  multiple="true" size="10"></select>
    </td>
  </tr>
</table>
</form>

Now, the original Javascript function need a slight alteration to allow for the option’s index to be passed into it. This only runs if the value of idx is not empty.

UPDATED: June 25, 2008 11:45PM

Altered version of moveOption()

function moveOption( fromID, toID, idx ) {
  if (isNaN(parseInt(idx))) {
    var i = document.getElementById( fromID ).selectedIndex;
  } else {
    var i = idx;
  }
  var o = document.getElementById( fromID ).options[ i ];
  var theOpt = new Option( o.text, o.value, false, false );
  document.getElementById( toID )
    .options[document.getElementById( toID ).options.length] = theOpt;
  document.getElementById( fromID ).options[ i ] = null;
}

All we need to do is to loop over the entire array of options in the requested select menu and move only the options that are selected. When an option is selected, rather than rewrite code, this function will tell the original function which option to move.

Move multiple options

function moveOptions( fromID, toID ) {
  for (var x = document.getElementById( fromID ).options.length - 1; x >= 0 ; x--)   {
    if (document.getElementById( fromID ).options[x].selected == true) {
      moveOption( fromID, toID, x );
    }
  }
}

UPDATED: June 25, 2008 11:45PM

So what did I forget? I forgot to have moveOption() actually use the third argument I passed to it.

If I am limited to choosing a single select option at a time, idx, the third argument in moveOption(), will be undefined. So if idx is “not a number”, then I’m going to move the selectedIndex of the “from” menu.

if (isNaN(parseInt(idx))) {
  var i = document.getElementById( fromID ).selectedIndex;
}

If idx is numeric, then I already know when index of the “from” menu needs to be moved.

else {}
  var i = idx;
}

So now i can be properly defined and moved in either single or multiple select menus.

var o = document.getElementById( fromID ).options[ i ];

Why count down instead of counting up?

The only important piece of code here is the definition of the for loop.

Normally, you’d use something like this:

for (var x = 0; x < document.getElementById( fromID ).options.length; x++)

With 10 options, this evaluates to

for (var x = 0; x < 10; x++)

The problem is that as each option is moved from one menu to the other, it is also removed from the original menu. This means that the length of the option array changes as each option is removed.

If you start at the first option (0) and moving towards the tenth option (9) as each option is removed, the remaining elements shift position in the array. So after the first option is moved, you’ll get the wrong options moved afterwards and eventually error out once the value of x goes past the length of the altered array.

So instead we start with the last element and step through it in reverse.

for (var x = document.getElementById( fromID ).options.length - 1; x >= 0 ; x--)

With 10 options, this evaluates to

for (var x = 9; x >= 0 ; x--)

Now you’ll always move the correct option and never encounter an error.

About the Author
Adrian J. Moreno

Adrian is an enterprise solution architect and full stack developer. Which stack depends on which system is on fire at the time. More information