Lesson 21 / 25
Input Types
The distinction between text, number, date, choice, and file fields; how much type information survives submission, and the shape values take when they reach the server.
Contents
The previous lesson built a form with a single kind of field. But the data to be collected is
not always free text: a number, a date, a time, a single choice, a multiple choice, long text,
a file. The input element’s type attribute makes this distinction.
Choosing a type affects three things at once: the interface the user sees, the validation the browser applies, and the shape the value takes on submission. This lesson focuses on the third, because the first two are only understood correctly once the third is known.
Three Effects of a Type
Once a type is declared, the browser offers a matching interface: increment buttons on a
number field, a calendar on a date field, a picker on a color field. These interfaces vary by
browser and device; on narrow screens, the type declaration also determines the keyboard
layout — digit keys for a number, a layout including the @ character for email.
A type also brings a constraint: submission is blocked when an invalid value is entered. This behavior is the subject of the next two lessons.
The third effect is the submission format, and it is defined.
How Values Reach the Server
// input-values.mjs — the values different types produce and how they look on the server import { createServer } from "node:http"; // An unchecked checkbox and a disabled field never enter the entry list at all. const entryList = [ ["name", "North Slope"], // type="text" ["temperature", "-4.2"], // type="number" ["measurementDate", "2024-03-12"], // type="date" ["measurementTime", "07:30"], // type="time" ["threshold", "18"], // type="range" ["verified", "on"], // checked type="checkbox", value not written ["source", "automatic"], // selected type="radio" ["tag", "humidity"], // <select> selected option ["tag", "wind"], // <select multiple> second selected option ["note", "first line\r\nsecond line"], // <textarea>: line endings become CRLF ]; const server = createServer((request, response) => { const chunks = []; request.on("data", (chunk) => chunks.push(chunk)); request.on("end", () => { const body = Buffer.concat(chunks).toString(); console.log("raw body:"); console.log(body); console.log("--- decoded pairs on the server ---"); for (const [key, val] of new URLSearchParams(body)) { console.log(key.padEnd(16), JSON.stringify(val), "(" + typeof val + ")"); } response.writeHead(204).end(); }); }); server.listen(0, "127.0.0.1", async () => { await fetch("http://127.0.0.1:" + server.address().port + "/record", { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: new URLSearchParams(entryList).toString(), }); server.close(); });
raw body: name=North+Slope&temperature=-4.2&measurementDate=2024-03-12&measurementTime=07%3A30&threshold=18&verified=on&source=automatic&tag=humidity&tag=wind¬e=first+line%0D%0Asecond+line --- decoded pairs on the server --- name "North Slope" (string) temperature "-4.2" (string) measurementDate "2024-03-12" (string) measurementTime "07:30" (string) threshold "18" (string) verified "on" (string) source "automatic" (string) tag "humidity" (string) tag "wind" (string) note "first line\r\nsecond line" (string)
The last column of the output is the lesson’s most important result: every value is a
string. Type information is not carried in submission. The server does not know that the
temperature field is a number field; it receives the string "-4.2", and converting it to a
number is its own job.
A direct consequence of this is that a type declaration is not a server guarantee. The topic is detailed in the fourth lesson.
Three more details are visible.
The date and time format is defined. Even though the interface the user sees uses a local
format, the value sent has the shape YYYY-MM-DD and HH:MM. The server can parse the value
without knowing which country the user is in.
The same name can appear more than once. The tag field was sent twice — the two selected
options of a multiple-selection list. On the server side, this field needs to be read as a
list; parsing that only takes the first value silently loses data.
Line endings in a long-text field become CRLF. %0D%0A appears in the body. Whatever
operating system the user is on, the value sent is the same.
The Set of Types
| Type | Data collected | Value sent |
|---|---|---|
text |
single-line text | the text entered |
search |
search term | the text entered |
email |
email address | the text entered |
url |
url | the text entered |
tel |
phone number | the text entered, format not checked |
password |
masked text | the text entered, unencrypted |
number |
number | period as the decimal separator |
range |
number from a range | number; the field does not display the value |
date, time, month, week |
date and time | in a defined format |
checkbox |
yes/no or multiple choice | value or on; nothing at all if unchecked |
radio |
single choice from a group | the value of the selected one |
file |
file | requires a separate encoding |
hidden |
invisible carried value | the value |
The password type only masks the value on screen. The body goes out unencrypted; protection
comes from the transport layer described in the Role of HTTPS lesson.
The range type does not show the selected value to the user, and is unsuitable when a value
needs to be entered precisely; it suits an imprecise setting.
The hidden type is not a privacy mechanism: its value is visible in the source text and can
be changed. It exists only to carry a piece of information back from the server.
Separate Elements
Three input types are not written with the input element.
textarea is multi-line text. Its initial value is given not through a value attribute but
through the element’s content.
select is a choice from a list; option elements carry the choices, and optgroup carries
groups of choices. The multiple attribute allows a multiple choice, and produces the
repeated name seen in the output above.
<p><label for="tag">Tags</label> <select id="tag" name="tag" multiple size="4"> <optgroup label="Atmosphere"> <option value="humidity" selected>Relative humidity</option> <option value="wind" selected>Wind</option> </optgroup> <optgroup label="Surface"> <option value="soil">Soil temperature</option> </optgroup> </select></p> <p><label for="note">Description</label> <textarea id="note" name="note" rows="4">first line second line</textarea></p>
If an option’s value attribute is not written, the value sent is the option’s text. value
is written so that the sent value does not change when the visible text changes.
The output element carries the result of a computation and takes no part in submission; the
behavior layer is what does the computing.
Suggestion List
A field may need to accept free text while also suggesting ready-made options. The datalist
element combines these two behaviors: it carries options without restricting the field to
them.
<p><label for="sensor">Sensor code</label> <input id="sensor" name="sensor" type="text" list="sensor-list"> <datalist id="sensor-list"> <option value="HUM-01"> <option value="TMP-02"> <option value="WND-03"> </datalist></p>
The field’s list attribute names the list’s id. The user can pick a value from the list or
type their own; the value sent is the field’s text either way. This is the distinction from
select: select restricts the choices, datalist only suggests them.
Whether the list appears and how it is presented depends on the browser. For this reason,
datalist content is written as a convenience, not a constraint; a field that genuinely needs
to be restricted uses select or a validation rule instead.
In the Station Document
<fieldset> <legend>Measurement information</legend> <p><label for="date">Measurement date</label> <input id="date" name="date" type="date"></p> <p><label for="time">Measurement time</label> <input id="time" name="time" type="time" step="600"></p> <p><label for="value">Measured value (°C)</label> <input id="value" name="value" type="number" step="0.1"></p> <input type="hidden" name="station" value="north-slope"> </fieldset>
The step="600" value on the time field is the markup equivalent of a ten-minute measurement
interval: six hundred in seconds. The hidden field carries the station code, and the user does
not need to enter it.
Summary
- The choice of type affects the interface, the validation, and the submission format together.
- Every value sent is a string; type information is not carried to the server, and converting it is the server’s job.
- Date and time values are sent in a defined format; the local format the user sees is not reflected in submission.
- An unchecked checkbox is never sent at all; a multiple choice produces the same name more than once.
- The
passwordandhiddentypes provide no privacy: the first only masks on screen, the second is visible in the source text.
Next Step
The fields are declared, but what each one wants is understood only from the text next to it. Until that text is declared as bound to the field in the document, the field is nameless: clicking the label with a mouse does not move focus to the field, and a screen reader announces the field as “editable text.” The next lesson builds that bond, and makes the cases where the bond is missing visible with an audit.
To keep your progress and take notes, Log in
My notes
Log in to take notes.