Tag: forms

Working with Forms in Two Functions

Interacting with forms – it’s a common need in JavaScript apps. You need to gather information from the form when it’s submitted; you need to initialize a form when it’s first displayed to the user. Here’s a simple form:

<form id="user">
    <input type="text" name="first-name" />
    <input type="text" name="last-name" />
    <input type="submit" name="submit" value="Submit" />
</form>

One way to gather the information would be to retrieve each input value individually.

var elements = document.forms["user"].elements;
var firstName = elements["first-name"].value;
var lastName = elements["last-name"].value;

But that soon becomes tedious as the size of the form grows. What we really want is a function that will allow us to easily retrieve all the data from a form. Something that might be used like this:

var userData = readFormData("user");
var name = userData["first-name"] + " " +
           userData["last-name"];

In a single function call, we could have all the inputs gathered into a JavaScript object. We could also conceive of a function that would write the data from a JavaScript object back into the form.

writeFormData("user", userData);

So, how would we implement such functions?

function readFormData(formIdOrName) {
    var formData = {};
    var elements = document.forms[formIdOrName].elements;
    for (var index = 0; index < elements.length; ++index) {
        var name = elements[index].name;
        formData[name] = elements[index].value;
    }
    return formData;
}

function writeFormData(formIdOrName, formData) {
    var elements = document.forms[formIdOrName].elements;
    for (var index = 0; index < elements.length; ++index) {
        var name = elements[index].name;
        elements[index].value = formData[name];
    }
}

After calling readFormData, the userData object might look something like this:

userData = {
    "first-name": "John",
    "last-name": "Smith"
}

The name attributes from the form elements have been used as property names and the value properties from the form elements have been assigned to those property names. But what if we want to add a checkbox to the form – will our functions still work? Not yet. Checkbox inputs store their state in the element’s checked property instead of the value property, so we need to do a bit more work. A couple of additional functions will help us gather information from checkboxes, dropdown lists, etc..

function getElementData(element) {
    // Special handling for <select> elements
    if (element.tagName === "SELECT") {
        return getElementSelections(element);
    }
    // Special handling for checkboxes and radio buttons
    if ((element.type === "checkbox") ||
        (element.type === "radio")) {
        return element.checked;
    }
    return element.value;
}

function getElementSelections(element)
{
    // Look at all the <option> elements and return the
    // selected options.
    var values = [];
    for (var index = 0; index < element.options.length;
         ++index)
    {
       if (element.options[index].selected) {
           if (element.multiple) {
               // Multiple selections are returned in
               // an array.
               values.push(element.options[index].value);
           } else {
               // Single selections are just returned as
               // a string.
               return element.options[index].value;
           }
       }
    }
    return values;
}

function setElementData(element, data) {
    if (element.tagName === "SELECT") {
        return setElementSelections(element, data);
    }
    if ((element.type === "checkbox") ||
        (element.type === "radio")) {
        element.checked = data;
    }
    element.value = (data === undefined) ? "" : data;
}

function setElementSelections(element, data)
{
    // If the data isn't submitted as an array, we can
    // create an array of one element to simplify the
    // selection logic.
    if (!(data instanceof Array)) {
        data = [data];
    }
    for (var index = 0; index < element.options.length;
         ++index)
    {
      if(data.indexOf(element.options[index].value) >= 0) {
        element.options[index].selected = true;
      }
    }
}

Now, with a couple of tweaks to our read and write functions, we can easily transfer data between a form and a JavaScript object.

function readFormData(formIdOrName) {
    var formData = {};
    var elements = document.forms[formIdOrName].elements;
    for (var index = 0; index < elements.length; ++index) {
        var name = elements[index].name;
        formData[name] = getElementData(elements[index]);
    }
    return formData;
}

function writeFormData(formIdOrName, formData) {
    var elements = document.forms[formIdOrName].elements;
    for (var index = 0; index < elements.length; ++index) {
        var name = elements[index].name;
        setElementData(elements[index], formData[name]);
    }
}

Combined with JSON.parse, JSON.stringify, and some AJAX calls, readFormData and writeFormData can be used to easily send and receive form data between the browser and a web server. We can even package everything up into a nicely wrapped object to look something like this:

function FormData(formIdOrName) {
    this.form = document.forms[formIdOrName];
}

FormData.prototype.readData = function () {
  ...
}

FormData.prototype.writeData = function (data) {
  ...
}

var userFormData = new FormData("user");
var userData = userFormData.readData();

Hopefully, this will simplify some of your work with forms in JavaScript. You can experiment with the final sample code on CodePen.