i Know Kung Foo Consulting

Move options between Select Menus with Javascript

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:

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

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

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

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

3. 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 );

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

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

5. 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.

Altered version of moveOption() UPDATED: June 25, 2008 11:45PM
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.


Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Mark's Gravatar Here is one I prepared earlier.

<a href="http://www.markireland.com.au/blog/client/index.cf...;
>order-a-slushie</a>
# Posted By Mark | 6/7/08 3:34 AM
Steve Good's Gravatar Great Post Adrian! I did notice that on the multiple select move, when moving items from right to left, the last item selected is not moved. I don't have a solution, but I thought I'd point that out.

Again, great post otherwise!
# Posted By Steve Good | 6/7/08 2:11 PM
Massimo Foti's Gravatar I had a dedicated JS library that does this and more. You can find it here:
http://www.massimocorner.com/libraries/spry/linked...

Not sure if it would fit your needs. Hope it could help somewhat

Massimo
# Posted By Massimo Foti | 6/10/08 10:24 AM
dexter.ba's Gravatar There is an error in if condition.
if (idx != "") should be
if (idx >=0)
and moving the last item in the list should now work:)
# Posted By dexter.ba | 6/25/08 11:56 AM
Adrian J. Moreno's Gravatar @dexter.ba Close, but that only allows the function moveOption() to work on the 2nd form and not both. Thanks for submitting the comment, I'd forgotten I needed to fix this post and you put me on the right track.

I've updated the entry with code that will work for both single and multiple select menus. I knew I was forgetting something when I posted the entry. Updates are marked.
# Posted By Adrian J. Moreno | 6/26/08 3:02 AM
stretch's Gravatar is there a way to submit on any change to the right side list?
# Posted By stretch | 10/7/08 4:26 PM
Alston's Gravatar Great Post, really helped me a lot, Thanks buddy
# Posted By Alston | 12/4/09 10:46 AM