009: Forms II
Learning outcomes
By the end of this lesson, you can add radio buttons, checkboxes, select menus, text areas, date and number controls; group related choices with fieldset and legend; explain single versus multiple selection; and use submit/reset controls intentionally.
Prerequisites and retrieval
Open 008's registration form. For each control, point to its visible label, unique ID, submission name, and current value. Predict which request data disappears when name is removed.
Terminology
- Radio group: Same-named radio inputs where at most one can be checked at a time. — Source: WHATWG: Radio Button state
- Checkbox: An input in the checkbox state: a binary on/off control, or one member of a same-name set. — Source: WHATWG: Checkbox state
- Select: “The select element represents a control for selecting amongst a set of options.” — Source: WHATWG: The select element
- Option: “The option element represents an option in a select element.” — Source: WHATWG: The option element
- Textarea: “The textarea element represents a multiline plain-text edit control for its raw value.” — Source: WHATWG: The textarea element
- Fieldset: “The fieldset element represents a set of form controls optionally grouped under a common name.” — Source: WHATWG: The fieldset element
- Legend: “The legend element represents a caption for the rest of the contents of the fieldset element’s parent fieldset element.” — Source: WHATWG: The legend element
- Reset: input type=reset restores all controls to their initial values without clearing server data. — Source: WHATWG: Reset Button state
- Initial value: The default value or selected/checked state declared in markup that reset restores. — Source: WHATWG: Form elements
- Radio button (
input type=radio): "The input element with type radio represents a radio button — a control that allows selection of a single value from a set." — Source: WHATWG: Radio Button state - Select multiple: "The select element with multiple attribute allows multiple options to be selected." — Source: WHATWG: Select element
- Optgroup: "The optgroup element represents a group of option elements with a common label." — Source: WHATWG: Optgroup element
Mental model: label the question and every answer
A set of radio buttons has two layers: the legend asks the shared question; individual labels name each answer. Same name makes radios mutually exclusive, while distinct IDs keep label associations unique.
<fieldset>
<legend>Preferred contact method</legend>
<input type="radio" id="contact-email" name="contact-method" value="email">
<label for="contact-email">Email</label>
<input type="radio" id="contact-phone" name="contact-method" value="phone">
<label for="contact-phone">Phone</label>
</fieldset>
If Email is selected, the submitted pair is contact-method=email. Giving each radio a different name would allow both to be selected, defeating the single-choice model.
Control choices
Use a checkbox for an independent yes/no choice:
<input type="checkbox" id="updates" name="updates" value="yes">
<label for="updates">Send occasional project updates</label>
Unchecked checkboxes submit no entry. The server must interpret absence correctly; hidden-field patterns are application-specific. For several independent interests, repeat the same name with different values if the server expects multiple entries.
Use select for a constrained option list:
<label for="topic">Main topic</label>
<select id="topic" name="topic">
<option value="html">HTML review</option>
<option value="accessibility">Accessibility audit</option>
</select>
Visible option text is user-facing; value is server-facing. Without value, option text is submitted. Native select is generally preferable to a custom scripted replacement.
Use textarea for multiline text:
<label for="message">Message</label>
<textarea id="message" name="message" rows="6" cols="40"></textarea>
Its initial value is text between tags, not a value attribute. Whitespace placed there can become initial content. rows and cols provide intrinsic sizing hints; CSS later controls layout.
type="date" provides a localized date UI while submitting a normalized date string such as 2026-09-01. Browser presentation varies. type="number" is for quantities where numeric stepping/range makes sense, not phone numbers, postal codes, card numbers, or identifiers. Those are text-like and may have leading zeros or non-numeric characters.
More native controls and input hints
Before building a custom JavaScript widget, check whether HTML already has an appropriate control.
Group long option lists with optgroup
<label for="branch">Preferred branch</label>
<select id="branch" name="branch">
<optgroup label="North">
<option value="n1">North Central</option>
<option value="n2">North Market</option>
</optgroup>
<optgroup label="South">
<option value="s1">South Station</option>
</optgroup>
</select>
Offer suggestions with datalist
<label for="city">City</label>
<input id="city" name="city" list="city-options">
<datalist id="city-options">
<option value="Chennai">
<option value="Madurai">
<option value="Coimbatore">
</datalist>
Unlike select, a datalist normally allows values outside the suggestions unless other validation restricts the input.
Hint the mobile keyboard with inputmode
inputmode does not validate data; it suggests an appropriate on-screen keyboard:
<label for="otp">One-time code</label>
<input id="otp" name="otp" inputmode="numeric" autocomplete="one-time-code">
Use semantic input types first. Add inputmode when the desired keyboard differs from what the input type alone provides.
The multiple attribute is supported by selected controls such as email and file inputs. Its meaning depends on the control, so confirm the element's contract rather than assuming all inputs support it.
Guided example: extend registration
Replace the form with:
<form action="/register" method="post">
<p>
<label for="full-name">Full name</label>
<input type="text" id="full-name" name="full-name" autocomplete="name">
</p>
<p>
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email">
</p>
<fieldset>
<legend>Preferred contact method</legend>
<p>
<input type="radio" id="method-email" name="contact-method" value="email">
<label for="method-email">Email</label>
</p>
<p>
<input type="radio" id="method-phone" name="contact-method" value="phone">
<label for="method-phone">Phone</label>
</p>
</fieldset>
<fieldset>
<legend>Topics of interest</legend>
<p>
<input type="checkbox" id="interest-html" name="interest" value="html">
<label for="interest-html">HTML</label>
</p>
<p>
<input type="checkbox" id="interest-a11y" name="interest" value="accessibility">
<label for="interest-a11y">Accessibility</label>
</p>
</fieldset>
<p>
<label for="experience">Years of coding experience</label>
<input type="number" id="experience" name="experience" min="0" max="80" step="1">
</p>
<p>
<label for="start-date">Preferred start date</label>
<input type="date" id="start-date" name="start-date">
</p>
<p>
<label for="message">What would you like to learn?</label>
<textarea id="message" name="message" rows="6" cols="40"></textarea>
</p>
<p>
<button type="submit">Create account</button>
</p>
</form>
Select Phone, both interests, experience 1, and a date. Trace entries. The radio contributes one value; repeated interest contributes two; the number and date contribute text representations to form submission. Browsers do not turn form data into strongly typed server values.
Tab through the group. Arrow-key behavior selects radios in a native group; Space toggles a focused checkbox. Exact behavior can vary by browser/platform, so test rather than replacing native controls.
Intermediate example: complete contact form
<form action="/contact" method="post">
<p>
<label for="contact-name">Name</label>
<input type="text" id="contact-name" name="name" autocomplete="name">
</p>
<p>
<label for="contact-email">Email</label>
<input type="email" id="contact-email" name="email" autocomplete="email">
</p>
<p>
<label for="contact-topic">Topic</label>
<select id="contact-topic" name="topic">
<option value="project">Project question</option>
<option value="feedback">Portfolio feedback</option>
<option value="other">Other</option>
</select>
</p>
<fieldset>
<legend>Reply preference</legend>
<input type="radio" id="reply-email" name="reply" value="email">
<label for="reply-email">Email reply</label>
<input type="radio" id="reply-none" name="reply" value="none">
<label for="reply-none">No reply needed</label>
</fieldset>
<p>
<label for="contact-message">Message</label>
<textarea id="contact-message" name="message" rows="8" cols="50"></textarea>
</p>
<button type="submit">Send message</button>
</form>
The button says what happens, not generic “Submit.” Avoid a reset button by default. Users can activate reset accidentally and lose work. If a genuine reset requirement exists, use <button type="reset">Reset form</button>, label it clearly, place it away from submit, and understand that it returns controls to markup defaults rather than undoing submitted server changes.
Advanced optional extension: defaults and multiple selection
checked sets an initial radio/checkbox state; selected sets an initial option. Choose defaults only when they do not manipulate consent. Never precheck optional marketing consent.
<select multiple> permits multiple options but can be difficult to discover and operate, especially without clear instructions. A checkbox group is often more understandable for a short list. If using multiple select, label it, explain platform-independent interaction as far as possible, test keyboard/touch/assistive technology, and ensure the server accepts repeated values.
Common mistakes and debugging
- Different radio names: they stop behaving as one group.
- Same ID repeated: labels target ambiguously; IDs remain unique.
- No fieldset/legend: the shared question may be lost.
- Checkbox assumed to submit
false: unchecked means absent. - Phone as number: use
type="tel"or text; phone numbers are identifiers. - Textarea
valueattribute: initial content belongs between tags. - Empty first option used as a label without guidance: give the select a real label; validation arrives tomorrow.
- Reset beside submit: omit unless there is proven value.
- Prechecked consent: require an intentional user choice.
Accessibility, security, and performance
Native grouping communicates relationships and supports keyboard interaction. Legends should be concise questions; labels should make each option understandable. Do not depend on layout alone. Date controls differ across user agents, so provide visible format guidance when exact input is important and validate on the server.
Every submitted value is untrusted, including select options and hidden/default values that users can alter. Limit collection, use HTTPS, validate and authorize server-side, and encode data on output. Contact forms need spam/rate controls that do not create inaccessible puzzles. Native controls are performant and robust; custom selects often add large scripts and accessibility defects.
Tiered exercises
Level 1: choices
Build one radio group for contact method and one checkbox group for interests. Predict entries for each state.
Level 2: complete
Add select, textarea, date, number, submit, and an intentionally omitted reset to the registration/contact page.
Level 3: evaluate
Keyboard-test groups, inspect submitted entries, explain unchecked behavior, and compare checkboxes with a multiple select for three interests.
Level 1: use the guided fieldsets. Selecting Email produces contact-method=email; selecting both interests produces interest=html and interest=accessibility; selecting none produces no interest entry.
Level 2: either complete guided registration or intermediate contact form satisfies the markup. Number is used only for years, date for a calendar value, select for constrained topic, textarea for multiline message, and the submit button states its action. Reset is omitted to protect entered work.
Level 3: Tab enters native groups; arrows move/select radios and Space toggles checkboxes according to platform conventions. Checkboxes are clearer for three visible independent choices. A multiple select may save space but needs extra operating knowledge and testing, so it is not automatically “advanced” or better.
Recap and exit questions
Radio buttons represent one choice, checkboxes independent choices, select constrained options, textarea multiline text, and date/number specialized values. Fieldset and legend preserve group meaning.
- What makes radio buttons mutually exclusive?
- What does an unchecked checkbox submit?
- Where is a textarea's initial value written?
- Why is a phone number not
type="number"? - Why are reset buttons usually omitted?
Try it with your own example
Custom-order forms are where radio groups, checkboxes, and select menus genuinely earn their keep, because each represents a real decision Rina's customers make. Build this one yourself before checking the answer.
Rina wants a "custom cake" order form. A customer picks exactly one size (radio), any number of add-ins (checkboxes), and a pickup date. Try writing it first, then compare:
<form action="/order-cake" method="post">
<fieldset>
<legend>Cake size</legend>
<input type="radio" id="size-6" name="size" value="6-inch">
<label for="size-6">6-inch (serves 6–8)</label>
<input type="radio" id="size-8" name="size" value="8-inch">
<label for="size-8">8-inch (serves 10–12)</label>
</fieldset>
<fieldset>
<legend>Add-ins</legend>
<input type="checkbox" id="addin-choc" name="addin" value="chocolate-shavings">
<label for="addin-choc">Chocolate shavings</label>
<input type="checkbox" id="addin-nuts" name="addin" value="toasted-nuts">
<label for="addin-nuts">Toasted nuts</label>
</fieldset>
<p>
<label for="pickup-date">Pickup date</label>
<input type="date" id="pickup-date" name="pickup-date">
</p>
<button type="submit">Send order request</button>
</form>
Now trace the submission yourself for a customer who picks the 8-inch size and both add-ins: you should get size=8-inch, addin=chocolate-shavings, and addin=toasted-nuts as three separate entries under the repeated addin name — not one combined value. If a customer picks no add-ins at all, the addin key is simply absent from the request, which is the same "unchecked means missing" behavior you saw with Rina's newsletter form's absent fields.
Further reading: MDN — Other form controls covers select, date, and number controls with more browser-by-browser rendering notes.
