DEV Community

Cover image for Unleashing the Hidden Power of HTML Forms
Mads Stoumann
Mads Stoumann

Posted on • Updated on

Unleashing the Hidden Power of HTML Forms

HTML forms are powerful beasts, but I feel a lot of its übercool features are often overlooked.

Typically seen just as tools for data submission, forms offer rich functionalities beyond inputs and validation.

Collections

All the inputs and outputs you put in a form, belongs to it's elements-collection:

  • <button>
  • <fieldset>
  • <input> (except type="image")
  • <output>
  • <select>
  • <textarea>
  • form-associated custom elements

This collection is live, meaning you can add and remove members, and it will be reflected in elements.length. Within the elements-collection you have namedItem. If you have a basic input within a form:

<form>
  <input name="firstname" value="John">
</form>
Enter fullscreen mode Exit fullscreen mode

You can grab it's value with:

form.elements.firstname.value
// or
form.elements.namedItem('firstname').value
Enter fullscreen mode Exit fullscreen mode

DISCLAIMER: To simplify the examples and make them more focused on what I write about, I haven't included <label>-elements, which you of course always should. Also, I don't use getElementById, but refer directly to elements with their id, which you shouldn't do IRL.


RadioNodeList

Another type of collection is RadioNodeList.
If you have a group of radio-buttons:

<form>
  <input type="radio" name="group" value="1">
  <input type="radio" name="group" value="2" checked>
  <input type="radio" name="group" value="3">
</form>
Enter fullscreen mode Exit fullscreen mode

You can simply grab the selected value with:

form.elements.group.value
Enter fullscreen mode Exit fullscreen mode

That's because the elements-collection detects that the item group is a RadioNodeList, and thus value returns the checked item.


Fieldsets

A <fieldset> has an elements-collection too.

<form id="customer">
  <fieldset name="delivery">
    <input name="firstname" value="John">
    <input name="lastname" value="Doe">
  </fieldset>
  <fieldset name="invoice">
    <input name="firstname" value="Jane">
    <input name="lastname" value="Smith">
  </fieldset>
</form>
Enter fullscreen mode Exit fullscreen mode

In this case, customer.elements.firstname.value will return nothing, because there's a conflict in naming with 2 elements matching name="firstname", but if we use:

customer.delivery.elements.firstname.value
customer.invoice.elements.firstname.value
Enter fullscreen mode Exit fullscreen mode

— we get "John" and "Jane", because we're now targetting the elements-collection within the fieldsets instead of the form.

DISCLAIMER: You shouldn't have multiple elements with the same name within a form, unless it's a radio-group. The example above is to illustrate the power of fieldsets and their elements-collection.


The Power of the form Attribute

The form-attribute allows you to physically place a form-control in one form, but make it belong to another form:

<form id="app">
  <input name="volume" type="range" value="85">
  <input form="prefs" name="store" type="checkbox" value="1">
</form>

<form id="prefs"></form>
Enter fullscreen mode Exit fullscreen mode

Now, go to a console and type:

app.elements.length //1
prefs.elements.length //1
Enter fullscreen mode Exit fullscreen mode

This way you can create "state collections" with a bunch of empty forms, while having all the inputs physically in the same "master" form (or "main app").

Add an oninput event-listener to the main form, and detect which form the current input belongs to :

app.addEventListener('input', (event) => {
  const input = event.target;
  switch(input.form) {
    case app:
      // Do something
      break;
    case prefs:
      // Do something else
      break;
  }
});
Enter fullscreen mode Exit fullscreen mode

Values

We already saw how value could return the value of the checked item, if the collection was RadioNodeList. But value has a few more tricks up it's sleeve.

Take this example:

<form id="app">
  <input name="firstname" value="John">
</form>
Enter fullscreen mode Exit fullscreen mode

When interacting with this input, we can retrieve two values:

app.elements.firstname.value
app.elements.firstname.defaultValue
Enter fullscreen mode Exit fullscreen mode

defaultValue returns the value that was set in HTML when the form was created, thus always giving you a "default state" to return to, should you need it.

When you reset a form with app.reset() or <button type="reset">, this is what a field is reset to.


valueAsNumber

All HTML attributes are strings, but if you're working with numeric form inputs, you can use valueAsNumber to directly get the input's numeric value:

<form id="app">
  <input name="height" type="range" min="50" max="230" value="180">
</form>
Enter fullscreen mode Exit fullscreen mode

Then, in JavaScript:

app.height.valueAsNumber //180
Enter fullscreen mode Exit fullscreen mode

If you use valueAsNumber on a non-numeric input, you'll get NaN (not a number).


valueAsDate

Similar to numeric inputs, date-type inputs have valueAsDate:

<form id="app">
  <input name="dob" type="date" value="2000-01-01">
</form>
Enter fullscreen mode Exit fullscreen mode

In JavaScript:

app.dob.valueAsDate
// Thu Jan 1 2000 01:00:00 GMT+0100 (Central European Standard Time)
Enter fullscreen mode Exit fullscreen mode

(you'll get a different timezone, depending on your location)

… and you can use the Date-object methods directly:

app.dob.valueAsDate.getUTCFullYear();
Enter fullscreen mode Exit fullscreen mode

Conclusion

In summary, HTML forms are not just containers for gathering user input but are dynamic interfaces with a wealth of underutilized capabilities.

Will you use the elements-collection in multiple forms or fieldsets to manage complex data-structures?

Top comments (2)

Collapse
 
tbroyer profile image
Thomas Broyer

… and if you want a real Date-object

It is already a date object: html.spec.whatwg.org/multipage/inp...

…so app.dob.valueAsDate.getUTCFullYear() will Just Work™

Collapse
 
madsstoumann profile image
Mads Stoumann

Thanks! — I missed that, have updated the text.