California Ajax Solutions Team

www.CalAST.com

Calast Input Elements Library

The InputElements Library

Kenneth F. Greenberg

This article describes the InputElements library, which I designed to leverage JavaScript objects that are usually used in web forms. I had developed these over the course of various projects. I wanted to reuse these as much as possible, to save time and to avoid the generally error-prone process of reuse through cutting and pasting source code. By developing objects and placing them in a library, it is only necessary to "get it right" once. As you might expect, this was done in an incremental way. Every time I needed a new object for a form I was designing, I just added it to the library once it was tested and known to be working. When I found a new feature that needed to be added, I examined all the other objects in the library to see if they needed this new method or property as well. I expect this will continue, as new projects generate new ideas for inclusion in this library.

The objects range from the trivial to the complex. Of course, the more complex the object is, the more time you save by instantiating it instead of reinventing it. But even the trivial objects benefit from a consistent API (Application Programming Interface) that lets you add all objects in essentially the same way. This is very much like the approach that Microsoft has taken with the ASP.NET controls - they enhance standard parts of a web page by adding a consistent set of features and API. The primary difference here is that the InputElements are implemented in JavaScript and run only on the client machine. Thus, updates happen instantly with no data transfers between client and server.

All the objects in this library were designed to be used as part of web page forms, but there is no reason they must be used in this way. You can add them to paragraphs, divisions, or pretty much any element in the DOM that needs an input of some kind. If you are using input elements (including complex ones) in forms, you just need to ensure that they have a name attribute and that they can pass their values to the server using any browser. If used elsewhere, then they need to have identifiers so they can be retrieved from JavaScript. Creating an InputElement automatically sets both of these things for you, so you never have to worry about forgetting one or the other.

In traditional OOP (Object Oriented Programming) style, InputElements are derived from a base class and add properties and methods appropriate to the control being specified. The base class, Input Element, implements the methods appendTo, enable, disable, onClick, and onChange. So all derived classes inherit these methods, as well as ones they specifically need. InputElements also includes a browser-agnostic function for attaching an event handler to the object.

Let's start with the most trivial example - the hidden element. This has no on-screen representation at all, but it is still quite useful in form submission. For example, you might want to pass the identifier of the currently selected customer to a server script. This might need to be used in a subsequent query, but the user probably never even knows what the ID might be - he or she thinks in terms of customer names. So it might have been selected from a list of customers (by name) and you need to send the corresponding identifier back to the server. Still, there's no need to show it on the display. You use a hidden input element for this, and it's so simple it exists here mostly for consistency. But it is a good illustration of how these objects work. Here's the code:

var secret = new InputElementHidden('customerID',currentCust);
secret.appendTo(myFormElement);

What we have done is to create an object of the Hidden input element type, assigned it an identifier that is used as both the name and id, and given it a value - since a hidden element is pointless without a value. We then added it into the DOM tree by appending it to the parent node myFormElement. Note that only the name is a string type; the value is usually a variable (although it could certainly be a string), and the parent element is always the DOM object itself, not its name.

Your reaction might be that there isn't much value here, but think of a more complicated object like a drop down list. This consists of a number of DOM elements - a select with multiple child nodes consisting of options. For cross-browser portability, each option should have a value as well as an on-screen display string. This is far more interesting and leads to some typical  things you can do with these objects. Here's one for picking out a language from a list:

var languages=['French','Spanish','German','Italian'];
var langList = new InputElementDDL('langList');
langList.setLabel('Language: ');
langList.addOptionList(languages);
langList.appendTo(myFormElement);

Here, we have a list of languages, perhaps built by retrieving the contents of a database table on the server side and inserted into JavaScript by a PHP script. Over time, this list may change - your code never has to. The server will always deliver a fresh copy, and you just use the list as delivered.

The constructor function takes one argument - the tag. This is very consistent throughout the library, except for the hidden element. Since that object has no on-screen representation, we use a second argument for its value; this could have been implemented as a setValue method, but we chose not to do so.

Since the drop-down list object does have an on-screen representation, it also includes a label. In this library, there is a separate object called an ElementLabel. These are added to other classes through composition, rather than inheritance. This makes sense, since an InputElement has a label but it is not one. The ElementLabel object manages display and alignment of labels through methods like setLabel, used in the above example. By attaching the label to the element, you can rearrange things to suit your design needs and the label always moves with the object. If you don't require one, just don't call setLabel, and no label will be displayed. But I prefer to attach an on-screen label to every element instead of it just being a separate text node.

The addOptionList method simply takes an array of strings and builds all the option nodes under the select that was created as part of the constructor function. Since Internet Explorer only does form submission properly if values are specified, this method adds the array elements as both display and value - i.e., <option value="French">French</option>. At the end, we add the completed object to its parent form element as usual. The appendTo method will cause the specified label text to be displayed with the default alignment, to the left of the object.

If you want more control, you can add options individually using the addOption method. This lets you specify value and display text separately, and to indicate which option is currently selected. For example, if French was somehow associated with the identifier 3 and was the default and currently selected language, you could specify langList.addOption('French',3,true,true). In fact, this addOption method uses the JavaScript built-in Option object, and passes its four arguments (text, value, default, and selected) directly to the Option constructor. It just always ensures that if you did not specify a value, one would be created for you from the specified display text.

Suppose you want to validate that the user really picked something before you send the form to the server. Your validation code can find out if the user chose anything with the getSelectedValue method:

if (langList.getSelectedValue() == -1) {
    alert('You forgot to pick a language');
    return false;
}

If you don't want to send the form to the server but just extract current values from the form and, say, build an XML or JSON string from it, you can just use the value returned from the object in your string.

And sometimes you might want to see if the first or last item in the list was selected, because they might have special meaning. For example, the last choice might be "none of the above." The DDL object keeps track of the number of rows that have been added to it, so you can not only get the value of the selected row, you could also get the number of the selected row to see if special processing is needed.

This works well enough for "static" uses of these object, but what if you need to do something as a result of the user making a selection? This possibility is addressed with event listener capability. You can use the onChange method to attach a listener (in a browser-independent way, of course) to your select. Then, when the user makes a choice, your listener will be invoked and you can do whatever your application requires.

Here is a table showing the methods for the InputElementDDL object (there are some new ones not yet added to this article).

InputElementDDL tag Constructor function, creates the select element.
appendTo parent DOM node Appends the label (if specified) and select to the specified DOM node
onChange function Attaches an event listener to the select.
addOption text, value, default, selected Adds an option element as a child to the select, with a value attribute and the specified display text.
addOptionList array of strings Adds option child nodes to the select, using each string in the array as both value and display text.
getSelectedRow none Returns the index of the selected item.
getSelectedValue none Returns the value of the selected item.
getRowCount none Returns the number of options currently in the object.
setLabel label Assigns a label string to the object for on-screen display
setTextAlign left or right Determines where the label will be placed with respect to the input; defaults to left
setAttribute muliple,true/false Sets the multiple attribute to either true or false, allowing multiple select
getOption index Returns the specified option choice as an object with value and text
select index Sets the currently selected index to the specified index
selectByValue value Sets the currently selected index to that of an option with the specified value

The InputElements library is a work in progress, but presently includes a number of element types. Included are textboxes, radio button groups, checkboxes, and buttons, as well as the objects described here. The manual is a bit of a work in progress, but may be viewed here. The current contents of the library are documented using the jsdoc toolkit, so an online API may be found here, and is probably more useful.

While many of these are very simple objects that can easily be produced in HTML or JavaScript, there are advantages to the library approach.

  1. The API is consistent for all objects.
  2. All the code is browser-independent
  3. All elements (except hidden) move with their labels, and are used in the same way.
  4. There are no dependencies (like getElementById) that impose ordering on use of the library, so you can build a form completely before adding it to the DOM.

In the future, I intend to keep extending the library to add more types and methods. For example, HTML5 types will be added as they become more common. Please feel free to revisit this page to see how things have evolved.