How To Move Items Between Lists with JavaScript

How To Move Items Between Lists with JavaScript

ntil recently, moving data between HTML lists was problematic due to differences in each browser’s DOM; however, as browsers move toward W3C DOM compliance, those differences are disappearing rapidly. In this article, I’ll show you how to use standards-based DOM methods and JavaScript to move data from one HTML list to another.

You’ve seen those pick lists in Windows where you select an item from one ListBox and it moves to a second ListBox (or vice-versa). How can you recreate that interface pattern in a browser?

Use the Document Object Model (DOM) and some JavaScript to control the list’s options array, and the task is easy.

The List Selection User Interface
This solution has been tested with IE 6, Mozilla 1.3, and Opera 7.01. The requirements for this application are:

  • Present the user with a list of available options.
  • Provide a second list to hold the user’s selected options.
  • Provide a mechanism for the user to add one or all options from the options list to the selected list.
  • Provide a mechanism for the user to remove one or all of the options from the selected list.
  • Display each option in either list box, but not both.

In addition, I’d like to display each of the items in one list or the other all the time so that users don’t have to scroll to view any item. Figure 1 shows how the sample application looks running in Mozilla.

Figure 1: Here’s how the sample application looks running in Mozilla. It looks similar in the other tested browsers.

Set Up the HTML
Open your favorite text editor and enter the following HTML:

      List Box Demo        

Save the file as option.htm.In this example, I’ve hard-coded the options available but you can populate the options dynamically and the solution will work just fine.

Start the Script
As you can see from the HTML, you’ll need to implement a few methods in the script. Create a new file in your text editor and start by entering the following JavaScript:

var selectedList;var availableList;function createListObjects(){    availableList = document.getElementById("availableOptions");    selectedList = document.getElementById("selectedOptions");}

I've set up two global object variables in the createListObjects() method?one for the available options and one for the user-selected options. The code calls the createListObjects() method from the onLoad event of the body tag so that you can later access the properties and methods of the objects anywhere in the script.Next, add the methods that move a single option between the lists.

function delAttribute(){   var selIndex = selectedList.selectedIndex;   if(selIndex 

The delAttribute() and addAttribute() methods are nearly identical. First, the script checks whether the user has selected an option from the source list. If no option is selected, the methods do nothing, they just return. Next, the code calls the appendChild() method on the target list, passing the item selected. Finally the code calls two helper methods named selectNone(), which resets the UI, removing any current selections, and setSize(), which resizes both list boxes to eliminate scrolling.

Note that it appears that the code removes nothing from either list. The appendChild() method takes a Node object as an argument. Each Node object can have only a single parent reference; appendChild() changes the parent reference of the Node argument by adding it to the target list. Changing the parent means the reference to the node's previous parent is lost, but because a Node object can have only a single parent, changing the parent has the additional effect of removing the node from the original list.

The Node object passed to appendChild() is passed by reference rather than by value. That is, the code passes the Node object itself to the method?not a copy of the Node's value. So, when you change the Node object, the DOM reflects those changes immediately. In fact, JavaScript?by default?passes all objects by reference. In contrast, JavaScript passes primitives (such as integers and strings) by value. Consider the following two functions:

function setTop(top){	document.getElementById      ('someLayer') = top;}function setLayerTop(lyr,top){ = top;}

Both the functions shown above take a "top" argument?a number that will be assigned to the top property of some layer. Both functions add the number and then forget it immediately. The number represents a value, not an object. However, setLayerTop adds an object argument. The lyr argument is a pointer to an object. When you set the lyr object's top property, it is stored in the object?and not forgotten the next time the property is accessed. Changing the object in the function changes the object permanently.

Finish the Script
Now that you've seen the power of passing objects to functions, add the following setSize() and selectNone() functions to the script (note that both functions take object arguments).

function setSize(list1,list2){    list1.size = getSize(list1);    list2.size = getSize(list2);}function selectNone(list1,list2){    list1.selectedIndex = -1;    list2.selectedIndex = -1;    addIndex = -1;    selIndex = -1;}

The setSize() function makes use of getSize(), which also takes objects as parameters.

function getSize(list){    /* Mozilla ignores whitespace,        IE doesn't - count the elements        in the list */    var len = list.childNodes.length;    var nsLen = 0;    //nodeType returns 1 for elements    for(i=0; i

Note that Mozilla and IE count nodes differently. Mozilla counts only the elements contained within the list. IE counts the white space as nodes as well. The getSize() function works around this by counting only the elements contained within the list (nodeType==1). In my opinion, Mozilla does it correctly?it ignores the white space. Also note that I've set the minimum size to 2. This allows me to avoid displaying either list as a drop down.Next, implement the mechanism that lets users move an entire list from one side to the other. Add the following to the script:

function delAll(){    var len = selectedList.length -1;    for(i=len; i>=0; i--){        availableList.appendChild(selectedList.item(i));    }    selectNone(selectedList,availableList);    setSize(selectedList,availableList);    }function addAll(){    var len = availableList.length -1;    for(i=len; i>=0; i--){        selectedList.appendChild(availableList.item(i));    }    selectNone(selectedList,availableList);    setSize(selectedList,availableList);    }

The only thing noteworthy about the delAll() and addAll() methods is that they both loop through the nodes from last to first. This approach avoids losing the reference to later nodes in the list. If the code looped from first to last instead, removing item(0) during each iteration, then after the first iteration, item(1) would have become item(0) and?would have been skipped?because the second time through the loop, the counter variable i equals 1. Therefore, the forward version would (inadvertently) skip nodesThe last function implemented is showSelected(). It loops through the user-selected options and displays the values. This function is largely superfluous and merely serves to demonstrate that the selected options have, in fact, been moved. In the real world, you'd just submit the user-selected options to your form handler as usual.

function showSelected(){    var optionList =        document.getElementById      ("selectedOptions").options;    var data = '';    var len = optionList.length;    for(i=0; i0)            data += ',';        data += optionList.item(i).value;    }    alert(data);}

I hope this exposure to the power of passing objects to functions helps to demonstrate JavaScript's inherent capabilities. I also hope that you see not only how this solution demonstrates list manipulation, but also that it illustrates the value of standards compliance. Standards compliance lets you, the developer, concentrate on what your application doesnot how it does it. As the major browser manufacturers implement more of the DOM and CSS standards in their products and users upgrade to the latest versions, scripting will become increasingly productive.


Share the Post: