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:
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.