Module: HTML
HTML·008·9 MIN READ

008: Forms I

TOPICS COVERED: Forms I

Learning outcomes

By the end of this lesson, you can build a basic registration form with form, explicit labels, inputs, and buttons; explain action, method, name, and value; choose text, email, and password types; and trace successful controls into submitted name/value pairs.

Prerequisites and retrieval

Use the semantic portfolio. Recall why link text needs an accessible name and why a button performs an action rather than navigation. Trace an HTTP request from 001; a submitted form creates another request to a server endpoint.

Terminology

  • Form: “The form element represents a collection of form-associated elements, some of which can represent editable values.” — Source: WHATWG: The form element
  • Control: An interactive form-associated element (input, button, select, textarea) able to contribute submission entries. — Source: WHATWG: Form control infrastructure
  • Label: “The label element represents a caption in a user interface for the element’s value.” — Source: WHATWG: The label element
  • Endpoint: The submission URL supplied by the form’s action attribute. — Source: WHATWG: Form submission
  • Method: The HTTP verb (get or post) selecting how form entries are submitted. — Source: WHATWG: method attribute
  • Name/value pair: The key and data each successful control contributes to the submitted entry list. — Source: WHATWG: Constructing the entry list
  • Successful control: A control eligible to contribute entries when the form is submitted (not disabled, checked, named…). — Source: WHATWG: Form control infrastructure
  • Accessible name: The programmatic label exposed to assistive technology, usually from an associated label. — Source: W3C WAI: Labeling controls
  • Autocomplete token: A standardized token (email, name, new-password…) hinting the expected kind of user data. — Source: WHATWG: Autofill
  • Input (input): "The input element represents a typed data field, allowing the user to edit the data." — Source: WHATWG: The input element
  • Form owner: "The form element that a form-associated element is associated with." — Source: WHATWG: Form owner
  • Enctype: "The enctype attribute specifies how form data is encoded for submission (e.g., application/x-www-form-urlencoded)." — Source: WHATWG: Form submission

Mental model: labeled fields become an envelope

A form is not merely boxes. Each control needs a human instruction (label), a programmatic submission key (name), and a current value. On submit, the browser constructs entries such as:

text
full-name=Asha Rao
email=asha@example.com
password=(entered value)

id connects a label and uniquely identifies an element in the document. name identifies data to the server. They often match for convenience but solve different problems. A control without name may remain usable on screen yet contributes no named form data.

form, action, and method

html
<form action="/register" method="post">
  <!-- controls -->
</form>

action resolves to the submission URL. method="get" encodes data in the URL query and suits safe retrieval/search operations. method="post" sends data in the request body and suits operations that change server state or include data inappropriate for a URL. POST is not encryption; use HTTPS. The server must process, validate, authorize, and safely store data.

For static course files without a server, use a clearly fictional endpoint such as /register and expect submission not to complete. Do not use a real third-party endpoint with personal test data.

Labels and inputs

html
<label for="full-name">Full name</label>
<input type="text" id="full-name" name="full-name" autocomplete="name">

for must exactly match one control's unique id. Clicking the label then focuses or activates the control, increasing the target and clarifying the relationship. Placeholder text is not a label: it disappears, may have poor contrast, and usually does not provide persistent instructions.

type="text" is general single-line text. type="email" offers email-oriented input behavior and later native format checking. type="password" visually obscures characters, but it does not encrypt data or make storage safe. Use meaningful autocomplete values:

html
<input type="email" autocomplete="email">
<input type="password" autocomplete="new-password">

Autocomplete can improve speed, reduce errors, and support users with cognitive or motor disabilities. Do not disable it reflexively. Authentication forms have specific current/new password tokens.

Buttons, names, and values

html
<button type="submit">Create account</button>

A button inside a form defaults to submit. State type="submit" for clarity. type="button" does nothing without script; type="reset" resets controls and is covered tomorrow. The button's visible content names the action.

For a text input, the user enters the value. A value attribute supplies an initial value; do not prefill personal or password data. For buttons and choice controls, value can define the submitted machine value. Never confuse a placeholder with a submitted value.

The submission contract: method, encoding, and control state

A form is a contract between the document and the endpoint receiving the data. Three details often cause real bugs:

  1. Only successful controls are submitted. A control generally needs a name to contribute data.
  2. Disabled controls are not submitted. If a value must be sent but should not be editable, readonly may be appropriate for supported text-like controls; do not assume disabled means “read-only and still submitted.”
  3. Encoding must match the data. Ordinary forms commonly use the default application/x-www-form-urlencoded; file uploads require multipart/form-data as lesson 017 shows.

Buttons also deserve an explicit type in reusable components:

html
<button type="submit">Create account</button>
<button type="button">Show password rules</button>

Inside a form, a plain <button> defaults to submit. An explicit type="button" prevents accidental submission when the button is only meant to trigger client-side behavior later.

Use autocomplete tokens where they accurately describe the field:

html
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email">

Correct autocomplete metadata can reduce typing and errors, especially on mobile and for users with cognitive or motor impairments.

Guided example: basic registration form

Create register.html with shared site landmarks:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Register | Asha Rao</title>
  </head>
  <body>
    <header>
      <p>Asha Rao</p>
      <nav aria-label="Primary">
        <ul>
          <li><a href="index.html">Home</a></li>
          <li><a href="about.html">About</a></li>
          <li><a href="register.html" aria-current="page">Register</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <h1>Register for project updates</h1>
      <p>Your password will be handled by a server in a later module.</p>
      <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>
        <p>
          <label for="password">Create password</label>
          <input type="password" id="password" name="password" autocomplete="new-password">
        </p>
        <button type="submit">Create account</button>
      </form>
    </main>
    <footer><p><a href="contact.html">Contact Asha</a></p></footer>
  </body>
</html>

Test label clicks. Press Tab and predict focus order from source. Enter fictional values. In network tools, submission may show the intended request even if the endpoint fails. Never enter a real password in a demo. Explain each entry: the email label identifies the control; id creates association; name="email" creates the submission key; the typed address becomes its value.

Using paragraphs to group a label/control pair is acceptable simple structure, though div would also be neutral. A paragraph cannot contain later block structures indiscriminately; keep each grouping straightforward.

Intermediate example: distinguish GET and POST

Build a safe site search:

html
<form action="/search" method="get">
  <label for="query">Search projects</label>
  <input type="search" id="query" name="q">
  <button type="submit">Search</button>
</form>

Searching “weather app” conceptually navigates to /search?q=weather+app. The URL is bookmarkable and shareable, appropriate for non-sensitive search state. Registration uses POST because it changes server state and includes credentials. However, POST data remains visible to the receiving server and tools and needs HTTPS in transit.

Temporarily remove name="q" and submit. The query is absent, demonstrating why name matters. Restore it. Temporarily break for="query"; clicking the label no longer focuses the field. Restore it. Debug one relationship at a time.

Advanced optional extension: submission details

Only successful controls contribute. Disabled controls are not submitted. Unchecked checkboxes contribute nothing. Buttons can contribute their own name/value only when used to submit. Duplicate names can intentionally produce multiple values, as with a checkbox group, but the server must expect them.

HTML form data is not a JavaScript object and does not automatically become JSON. Default encoding for ordinary POST forms is application/x-www-form-urlencoded; file uploads need multipart/form-data, covered later. This distinction prevents the myth that HTML chooses a database schema.

Predict the entry list, not just the visible controls. A submit button contributes only when it caused submission, and pressing Enter may submit using the form's default submitter. Give every button an explicit type; otherwise a button inside a form defaults to submit, which can produce an accidental request. For a form with name="email" and an unchecked name="updates" checkbox, the checkbox key is absent, not updates=false; the server must define that missing-value policy.

Common mistakes and debugging

  • Label with no matching control: verify exact for/id and unique IDs.
  • Placeholder as only label: add persistent visible label.
  • Missing name: inspect the submission entry list.
  • Duplicate IDs: run the conformance checker.
  • Password type treated as security: use HTTPS and secure server handling; masking is only visual privacy.
  • GET used for secrets: queries appear in URLs, history, logs, and referrals.
  • Button with unspecified intent: state type, especially reusable components.
  • Real data sent to demo endpoint: use fictional values and a controlled server.
  • Autocomplete disabled: provide accurate tokens unless a concrete exception exists.

Accessibility, security, and performance

Every control needs a programmatically associated label or equivalent accessible name; visible labels are the robust default. Label text should describe expected data. Source order should match reading and focus order. Native controls bring keyboard, touch, zoom, and assistive-technology behavior that generic scripted boxes lack.

Client markup cannot secure a form. Use HTTPS, server-side validation, output encoding, authorization, CSRF defenses where relevant, rate limits, and safe password hashing in the eventual application. Collect only necessary data and explain its use. Forms are lightweight, but avoid unnecessary third-party scripts that delay interaction or collect input. Correct autocomplete reduces effort and errors.

Tiered exercises

Level 1: associate

Create full-name and email controls. Give each unique id, matching label, useful name, correct type, and autocomplete token.

Level 2: register

Build the complete registration form with text, email, password, POST action, and explicit submit button. Trace the three name/value pairs.

Level 3: compare

Add a GET search form. Submit fictional values, inspect both request destinations, remove/restore one name, and keyboard-test labels and focus.

Level 1:

html
<label for="full-name">Full name</label>
<input type="text" id="full-name" name="full-name" autocomplete="name">
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email">

Level 2: use the guided form. For fictional input Sam Lee, sam@example.com, and a demo password, the keys are full-name, email, and password. POST places encoded entries in the body, not the URL, but only HTTPS protects transit and the server remains responsible for security.

Level 3: use the intermediate search. Its visible destination contains ?q=...; registration targets /register with POST. Without name="q", no q entry exists. Each label click focuses its control and Tab follows source order through controls and submit button.

Recap and exit questions

Forms gather labeled values and submit named entries to endpoints. IDs associate labels; names identify submitted data; types provide native behavior; methods express request intent.

  1. Why are id and name not interchangeable?
  2. When is GET suitable?
  3. Why is a placeholder not a label?
  4. Does type="password" encrypt a value?
  5. What happens to a control without name?
  6. Why can an apparently harmless button submit a form?

Try it with your own example

Build one more form now, on your own, before the next lesson adds more control types — a short one, so any mistake in your for/id matching is easy to spot and fix yourself.

Rina wants a simple newsletter signup at the bottom of her homepage: just a name and an email, POSTing to a fictional endpoint. Write it from the pattern above without copying it line for line:

html
<h2>Get weekly bake announcements</h2>
<form action="/subscribe" method="post">
  <p>
    <label for="subscriber-name">First name</label>
    <input type="text" id="subscriber-name" name="first-name" autocomplete="given-name">
  </p>
  <p>
    <label for="subscriber-email">Email address</label>
    <input type="email" id="subscriber-email" name="email" autocomplete="email">
  </p>
  <button type="submit">Subscribe</button>
</form>

Now break it deliberately, the way you did with links in lesson 004: change for="subscriber-email" to for="subscriber-mail" (one letter off) and click the label. Nothing happens — the field doesn't focus. That silent failure is exactly what a real typo looks like in production, and now you'll recognize it instantly instead of spending twenty minutes confused the first time it happens on a client's site.

Further reading: MDN — Sending form data shows what the actual HTTP request body looks like for a form exactly like this one.

Official references