Form Structure: form, action and method

Every form is a container, a set of named controls and a submit button. Learn what action and method do, why name matters more than id, and what the browser sends.

Concept

A form collects values from a reader and sends them somewhere. The form element is the container, and it carries the two attributes that decide where the values go and how they travel.

A rendered form labelled with its elements: a form with action and method, a fieldset with a legend, labels bound to inputs through for and id, a required field, and a submit button, plus a flow diagram showing validation, collection of name and value pairs, and the difference between GET in the URL and POST in the request body.
The parts of a form, and what the browser does when it is submitted.

Syntax

<form action="/apply" method="post">
  <!-- labelled controls -->
  <button type="submit">Send</button>
</form>
AttributePurpose
actionThe URL the values are sent to. Omitted means the current page.
methodget or post. Defaults to get.
enctypeHow the body is encoded. Needed as multipart/form-data for file uploads.
novalidateSwitch off the browser built in validation.
autocompleteon or off for the whole form.
targetWhere to show the response. Rarely useful.

The name attribute is what gets submitted

This trips up almost everyone once. A control is submitted under its name, not its id. The id exists so a label can point at it and so scripts and CSS can find it. They are two different jobs and both are usually needed.

<label for="email">Email address</label>
<input type="email" id="email" name="email_address">

The server receives email_address=.... The id never leaves the browser.

A control with no name is not submitted at all. It is the first thing to check when a value mysteriously fails to arrive.

What the browser sends

On submit, the browser gathers every control that is:

  • inside the form (or associated with it through a form attribute),
  • has a name,
  • is not disabled,
  • and, for checkboxes and radios, is checked.

Those become name and value pairs. With method="get" they are appended to the action URL as a query string. With method="post" they travel in the request body.

GET  /search?q=brass+lamp&sort=price
POST /apply        (body: name=Meera+Iyer&email=meera%40example.com)

Example

<form action="/enquiry" method="post">
  <fieldset>
    <legend>Your details</legend>

    <p>
      <label for="name">Full name</label>
      <input type="text" id="name" name="name" required autocomplete="name">
    </p>

    <p>
      <label for="email">Email address</label>
      <input type="email" id="email" name="email" required autocomplete="email">
    </p>
  </fieldset>

  <fieldset>
    <legend>Your enquiry</legend>

    <p>
      <label for="course">Course of interest</label>
      <select id="course" name="course">
        <option value="">Please choose</option>
        <option value="design">Design</option>
        <option value="data">Data science</option>
      </select>
    </p>

    <p>
      <label for="message">Message</label>
      <textarea id="message" name="message" rows="5"></textarea>
    </p>
  </fieldset>

  <input type="hidden" name="source" value="website">

  <button type="submit">Send enquiry</button>
</form>

Explanation

Each control has a label bound to it and a name for the server. The two fieldset elements group related fields and each carries a legend, which is announced before the fields inside it. The hidden field carries a value the reader never sees but the server needs. The submit button ends the form.

fieldset and legend

fieldset groups related controls; legend names the group and must be its first child.

<fieldset>
  <legend>Preferred contact method</legend>
  <label><input type="radio" name="contact" value="email"> Email</label>
  <label><input type="radio" name="contact" value="phone"> Phone</label>
</fieldset>

For radio buttons and checkbox groups this is not optional. Without a legend, a screen reader announces Email, radio button with no indication of what question is being answered. The legend supplies the question.

Do not wrap every single field in its own fieldset. Group what genuinely belongs together.

Forms outside the form element

The form attribute lets a control live anywhere in the document and still belong to a form:

<form id="filters" action="/search">...</form>

<!-- elsewhere on the page -->
<button type="submit" form="filters">Apply filters</button>

Useful when a design puts the action button in a sticky bar outside the form markup.

Important rules

  • Forms must not be nested. One form inside another is invalid and the parser will discard the inner one.
  • form is a block level element and may contain almost anything except another form.
  • A control with no name is never submitted.
  • A disabled control is never submitted; a readonly one is.
  • Pressing Enter in a text field submits the form via its first submit button. That is expected behaviour, not a bug.
  • File uploads require method="post" and enctype="multipart/form-data".

Common mistakes

  • Giving controls an id but no name, then wondering why the server received nothing.
  • Nesting forms, usually by accident when a template is included inside another.
  • Using disabled on a field whose value you still need. Use readonly.
  • Building a form out of div elements and a click handler, losing keyboard submit, autofill and validation.
  • Omitting method and accidentally sending sensitive values in the URL.
  • Placing a fieldset around every field, producing a wall of nested groups.

Best practices

  • Always state method explicitly, even when it is get.
  • Use a real form element even for forms handled entirely in JavaScript. You inherit validation, autofill and keyboard behaviour for free.
  • Group related fields in a fieldset with a legend, and always for radio and checkbox groups.
  • Add autocomplete tokens so browsers can fill fields correctly.
  • Keep one clear submit button, and place it at the end.
  • Validate on the server regardless of what the browser checks.

Practice

  1. Build an enquiry form with two fieldsets, five labelled fields and one submit button. Submit it with method="get" and read the query string.
  2. Remove the name from one field and observe what the server receives.
  3. Compare disabled and readonly on the same field and note which value arrives.
  4. Move the submit button outside the form and reconnect it with the form attribute.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Software Engineer Notes Management System Administrator

Continue reading

All HTML notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.