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>
(excepttype="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>
You can grab it's value with:
form.elements.firstname.value
// or
form.elements.namedItem('firstname').value
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 usegetElementById
, 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>
You can simply grab the selected value with:
form.elements.group.value
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>
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
— 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>
Now, go to a console and type:
app.elements.length //1
prefs.elements.length //1
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;
}
});
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>
When interacting with this input, we can retrieve two values:
app.elements.firstname.value
app.elements.firstname.defaultValue
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>
Then, in JavaScript:
app.height.valueAsNumber //180
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>
In JavaScript:
app.dob.valueAsDate
// Thu Jan 1 2000 01:00:00 GMT+0100 (Central European Standard Time)
(you'll get a different timezone, depending on your location)
… and you can use the Date-object methods directly:
app.dob.valueAsDate.getUTCFullYear();
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)
It is already a date object: html.spec.whatwg.org/multipage/inp...
…so
app.dob.valueAsDate.getUTCFullYear()
will Just Work™Thanks! — I missed that, have updated the text.