One of HTML's biggest flaws is it's forms structure. Namely, that there is a single tag, input, which is used for all methods of taking data in from the user. The problem with this, is that different DOM Attributes mean different things depending on the value of the 'type' attribute. They don't fire events as you might expect (why don't radio buttons fire Change events?). And what's available in the DOM is pretty simplistic. Select boxes do provide selectedIndex and value directives via the DOM, but what if you have the 'multiple' attribute set? There is no quick and easy way, from JavaScript, to grab every value from the list.
Recently, I've been rewriting our Class Lists application, to prompt the users with the list of courses they're authorized for, and allow them to select one or more course from their list to display. And, since I'm using MVC, I can redirect the user to a nice, bookmarkable URL. The benefit also is that I can send the user to one of those URLs via JavaScript. For my interface, I'm opting to remove a bunch of radio buttons and replacing them with links, where the HREF on the anchor tags is updated as the user's selection changes. However, that means that I have a multi-select box that I need to repeatedly query for it's members. Oh, and this list can be over a thousand options long under certain circumstances.
I'm using YUI3 for the implementation of this, so some of what I'm doing is going to be YUI3 specific, and I'm going to assume that arguments to my methods are YUI3 nodes.
Traditionally, you'd simply iterate over the options of the list, and look for selected items, like so:
This has the benefit of being fairly straight-forward, but it's not the best answer to the problem. For the best solution, and the more computer-sciency solution, we need to look into the realm of map-reduce. Part of the reason MapReduce is such a great solution to this problem is because many newer browsers are implementing map-reduce functionality in native code, allowing them to largely out perform functions like the one above, particularly on large datasets. Further, any good platform library will offer an implementation of the map and reduce functions that will be executed if the native call isn't available.
Run through this JavaScript implementation of JSMin at the 'conservative' level, the base implementation is 264 characters, and the reduce one is 262, a fairly negligible difference. These two function will return precisely the same values on the same selectBox.
A quick explanation of the Map and Reduce functionality:
As stated, Y.Array.map and Y.Array.reduce, a part of the collection module in YUI3, will defer to native implementations, if possible. The argument lists are as follows:
Y.Array.map:
- Array - This can't be a NodeList, hence the call the Y.NodeList.getDOMNodes
- function - The function to execute on each element of the argument array, this should return a single value.
- context - optional argument for the context to run the function under, defaults to the window object
Y.Array.reduce:
- Array - The array, generally output by the map function, that you wish to reduce
- Initial Value - The initial value for the reduce function, this will be the seed value for the first parameter of the argument function.
- Function - Function to execute to reduce the values.
For the function above, I'm just doing a simple reduce, since I'm not planning to do any manipulation of the values prior to reducing over them (in essence, I'm doing the 'identity mapping'). I did have a mapping function that would do the selected check, emitting the value if it was selected, and undefined otherwise, but it bought me nothing, while adding a fair amount of length to the function. This is due to the fact that each value must map to something, even if it's undefined (this is aided by the fact that, in JavaScript, every function implicitly returns undefined if it doesn't explicitly return something else).
Map-Reduce is a really interesting tool, and while it shines best in a clustering environment, on something like Hadoop, a good understanding of the technique can be helpful even on smaller applications. I will most likely have a more in-depth look at a more involved map-reduce problems up fairly soon.