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
view plain print about
1<form id="foo" name="foo" action="" onsubmit="return false;">
2<table>
3    <tr>
4        <td>
5            <select id="LEFT_MENU" size="10">
6                <option value="1">One</option>
7                <option value="2">Two</option>
8                <option value="3">Three</option>
9                <option value="4">Four</option>
10                <option value="5">Five</option>
11                <option value="6">Six</option>
12                <option value="7">Seven</option>
13                <option value="8">Eight</option>
14                <option value="9">Nine</option>
15                <option value="10">Ten</option>
16            </select>
17        </td>
18        <td valign="middle">
19            <p><input type="button" id="moveRight" value="&gt;" onclick="moveOption('LEFT_MENU','RIGHT_MENU')"></p>
20            <p><input type="button" id="moveLeft" value="&lt;" onclick="moveOption('RIGHT_MENU','LEFT_MENU')"></p>
21        </td>
22        <td>
23            <select id="RIGHT_MENU" size="10"></select>
24        </td>
25    </tr>
26</table>
27</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
view plain print about
1<script type="text/javascript">
2function moveOption( fromID, toID )
3{
4    var i = document.getElementById( fromID ).selectedIndex;
5    var o = document.getElementById( fromID ).options[ i ];
6    var theOpt = new Option( o.text, o.value, false, false );
7
8    document.getElementById( toID ).options[document.getElementById( toID ).options.length] = theOpt;
9    document.getElementById( fromID ).options[ i ] = null;
10}
11</script>

Let's go through this line by line:

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

view plain print about
1var i = document.getElementById( fromID ).selectedIndex;

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

view plain print about
1var o = document.getElementById( fromID ).options[ i ];

3. Create a new option object based on the select option.

view plain print about
1/*
2The Option object has 4 properties
3Option( text, value, defaultSelected, selected )
4*/

5var theOpt = new Option( o.text, o.value, false, false );

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

view plain print about
1document.getElementById( toID ).options[document.getElementById( toID ).options.length] = theOpt;

5. Remove the select option from the left menu.

view plain print about
1document.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
view plain print about
1<form id="foo2" name="foo2" action="" onsubmit="return false;">
2<table>
3    <tr>
4        <td>
5            <select id="LEFT_MENU2" multiple="true" size="10">
6                <option value="1">One</option>
7                <option value="2">Two</option>
8                <option value="3">Three</option>
9                <option value="4">Four</option>
10                <option value="5">Five</option>
11                <option value="6">Six</option>
12                <option value="7">Seven</option>
13                <option value="8">Eight</option>
14                <option value="9">Nine</option>
15                <option value="10">Ten</option>
16            </select>
17        </td>
18        <td valign="middle">
19            <p><input type="button" id="moveRight2" value="&gt;" onclick="moveOptions('LEFT_MENU2','RIGHT_MENU2')"></p>
20            <p><input type="button" id="moveLeft2" value="&lt;" onclick="moveOptions('RIGHT_MENU2','LEFT_MENU2')"></p>
21        </td>
22        <td>
23            <select id="RIGHT_MENU2" multiple="true" size="10"></select>
24        </td>
25    </tr>
26</table>
27</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
view plain print about
1function moveOption( fromID, toID, idx )
2{    
3    if (isNaN(parseInt(idx)))
4    {
5        var i = document.getElementById( fromID ).selectedIndex;
6    }
7    else
8    {
9        var i = idx;
10    }
11
12    var o = document.getElementById( fromID ).options[ i ];
13    var theOpt = new Option( o.text, o.value, false, false );
14    document.getElementById( toID ).options[document.getElementById( toID ).options.length] = theOpt;
15    document.getElementById( fromID ).options[ i ] = null;
16}

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
view plain print about
1function moveOptions( fromID, toID )
2{
3    for (var x = document.getElementById( fromID ).options.length - 1; x >
= 0 ; x--)
4    {
5        if (document.getElementById( fromID ).options[x].selected == true)
6        {
7            moveOption( fromID, toID, x );
8        }
9    }
10}

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.

view plain print about
1if (isNaN(parseInt(idx)))
2{
3    var i = document.getElementById( fromID ).selectedIndex;
4}

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

view plain print about
1else
2{
3    var i = idx;
4}

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

view plain print about
1var 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:

view plain print about
1for (var x = 0; x < document.getElementById( fromID ).options.length; x++)

With 10 options, this evaluates to

view plain print about
1for (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.

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

With 10 options, this evaluates to

view plain print about
1for (var x = 9; x >= 0 ; x--)

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