Wednesday, June 1, 2011

Useful Javascript snippets: Get multiple selections in a SELECT


Scenario: You have an HTML <select> list like so:
<select id="idAuthors" multiple="true" name="authors" size="12">
    <optgroup label="C">
        <option>Cummings, E E</option>
    </optgroup>
    <optgroup label="S">
        <option>Shakespeare, William</option>
        <option>Steinbeck, John</option>
    </optgroup>
    <optgroup label="T">
        <option>Talmage, James E</option>
        <option>Tolkien, J R R</option>
        <option>Tolstoy, Leo</option>
        <option>Twain, Mark</option>
    </optgroup>
</select>
This gives you the following:

You can choose one or more of these names. However, if you use Javascript to show the element's selectedIndex, it will only give you the first result:
<script language="javascript" type="text/javascript">
    alert(document.getElementById('idAuthors').selectedIndex);
</script>
If you select Shakespeare, Talmage, and Twain, the only result you will see is Shakespeare, William.
Solution: Grab the first selectedIndex from the <select>, and store it into an array. Then, de-select that entry. The next call to selectedIndex will yield the next result, Talmage, James E. Add that one to your array, and remove it. Lather, rinse, repeat.
At the end, none of the items will be selected anymore. You may want to be nice and re-select them all so the user doesn't have to (that could get pretty annoying for the user).
With everything stored in the array, simply return that array (or a string concatenated with all of the array entries).
Below is a code snippet:
function find_selected(id_name, id_out)
{
    // Names of all selected items, before they are de-selected by this script
    var option_name = new Array();

    // Indices of all selected items
    var option_index = new Array();

    var obj = document.getElementById(id_name);

    // Save the name and index of each selected item, then de-select the item
    while (obj.selectedIndex != -1)
    {
        option_name.push(obj.options[obj.selectedIndex].text);
        option_index.push(obj.selectedIndex);
        obj.options[obj.selectedIndex].selected = false;
    }

    // All of the items have now been de-selected; re-select all of them
    for (i = 0; i < option_index.length; ++i)
    {
        obj.options[option_index[i]].selected = true;
    }

    // Return the list of all selected items
    document.getElementById(id_out).value = option_name.join(';');
}
Hopefully, the comments make everything clear. You may also notice that I store both the name and its index. Referencing the option's index prevents our code from just finding the first occurance of the name; it re-highlights the same ones that the user selected. Finally, if you wish, you could return the array itself, or change the delimiter between the results.
If you plan on submitting these values to your server, you may find that pushing the results into an <input type="hidden"/> will let you see each one that was selected, and respond appropriately.
For a complete code solution, try this out:
<html>
<head>
<script type="text/javascript" language="javascript">
/**
   Finds all selected items from `id_name' and outputs the list to `id_out'.
   An example could be:
     <select id="idTxt" multiple="true">
       <option>One</option>
       <option>Two</option>
       <option>Three</option>
     </select>
     <input type="hidden" id="idHid" value="" />
   You would call "find_selected('idTxt', 'idHid');"
   Anything that is selected within 'idTxt' will be added to the value of the
   hidden 'idHid' input.
 */
function find_selected(id_name, id_out)
{
    // Names of all selected items, before they are de-selected by this script
    var option_name = new Array();

    // Indices of all selected items
    var option_index = new Array();

    var obj = document.getElementById(id_name);

    // Save the name and index of each selected item, then de-select the item
    while (obj.selectedIndex != -1)
    {
        option_name.push(obj.options[obj.selectedIndex].text);
        option_index.push(obj.selectedIndex);
        obj.options[obj.selectedIndex].selected = false;
    }

    // All of the items have now been de-selected; re-select all of them
    for (i = 0; i < option_index.length; ++i)
    {
        obj.options[option_index[i]].selected = true;
    }

    // Return the list of all selected items
    document.getElementById(id_out).value = option_name.join(';');
}
</script>

<title>Selection Example</title></head>
<body>

<form action="" onsubmit="alert(document.getElementById('idSelection').value); return false;">

<input type="hidden" name="selection" id="idSelection" />

<select id="idAuthors" multiple="true" name="authors" size="12">
    <optgroup label="C">
        <option>Cummings, E E</option>
    </optgroup>
    <optgroup label="S">
        <option>Shakespeare, William</option>
        <option>Steinbeck, John</option>
    </optgroup>
    <optgroup label="T">
        <option>Talmage, James E</option>
        <option>Tolkien, J R R</option>
        <option>Tolstoy, Leo</option>
        <option>Twain, Mark</option>
    </optgroup>
</select>

<input type="submit" value="Submit" onclick="find_selected('idAuthors', 'idSelection');" />
<input type="reset" value="Reset" />

</form>

</body>
</html>

No comments:

Post a Comment