1. Interaction
  2. Forms
  3. The form and its request

Interaction · Forms 01

The form and its request

A form is a small program written in markup. Given a destination, a method and some named controls, the browser collects what the person entered, encodes it, writes a complete HTTP request, sends it and shows the answer, with no script at all. Knowing exactly what it sends, and what it quietly leaves out, is the first step to building forms that work.

This page reads a form the way the browser does. It starts with the pieces every form is made of, then follows one submission from the button press to the response: which checks run, which fields are collected, how they are encoded, where they go, and which attributes on a button can change all of that. The demo at the end puts every surprise in one form and sends it to /api/echo, which answers with exactly what arrived.

The web's first interaction was the link: a fixed URL, followed on a click. Its second was the form. Fill-out forms arrived with NCSA Mosaic 2.0 around the turn of 1994 and were written into the HTML 2.0 standard (RFC 1866) in 1995, and they are still how people sign up, search, buy, report and apply.

The simplest way to understand a form is as a parameterized link. A link to a list of easy trails that allow dogs has its question built into the URL:

<a href="/trails/?difficulty=easy&amp;dogs=yes">Easy trails that allow dogs</a>

A form asks the same question but lets the person fill in the blanks. Each control supplies one parameter, and pressing the button builds the URL and follows it:

<form action="/trails/" method="get"> <label for="difficulty">Difficulty</label> <select id="difficulty" name="difficulty"> <option value="easy">Easy</option> <option value="moderate">Moderate</option> <option value="hard">Hard</option> </select> <input type="checkbox" id="dogs" name="dogs" value="yes"> <label for="dogs">Dogs allowed</label> <button>Find trails</button> </form>

Choose "Easy," tick the box, press the button, and the browser goes to /trails/?difficulty=easy&dogs=yes, the very URL the link held. The server cannot tell the two apart, and it does not need to. A form that only asks a question, such as a search or a filter, is exactly this: a link whose query string the person writes by choosing.

A form can do one thing a link cannot: send a request with a body, using POST, to change something on the server. Everything else on this page builds on those two ideas. A form with method="get" is a link with blanks; a form with method="post" is a letter the browser writes and mails for you. Patterns: The form already talks makes the same point from the network side: a form is already a complete HTTP client.

2. The pieces of a form

Every form, however long, is made of the same few kinds of parts. Learn to spot them and you can read any form, predict what it sends, and see what it is missing.

  • The <form> element holds everything and says where the data goes and how: its action and method. Section 3 covers its attributes.
  • Controls are the things a person fills in or chooses: <input> in its many types, <select>, <textarea>. Each control that sends data has a name, which becomes the key in the request, and a value, which is whatever the control holds when the form is submitted. Its id is for the page: labels, scripts and CSS find the control by it. Form Fields covers every control and exactly what each one sends.
  • Labels name the controls for people and for assistive technology. A <label> points at its control with for holding the control's id. A <fieldset> with a <legend> names a group of controls that answer one question, such as a set of radio buttons. Organizing Forms covers both.
  • Buttons submit the form. A <button> inside a form submits it unless its type says otherwise; type="button" makes a button that does nothing until a script gives it a job. A button can carry a name and value of its own, and attributes that change where the form goes, as section 8 shows.
  • Messages, hints before anyone types and errors after, go in <output> elements tied to their control. They are part of the page, never part of the request. Organizing Forms explains the pattern.

Here are all five in the smallest useful form. The <form-field> element holds one label, its control and its messages together, and the <br> elements keep it readable with no CSS; Organizing Forms explains why this course writes every field this way.

<form action="/api/echo" method="post"> <form-field> <label for="email">Email</label><br> <input id="email" name="email" type="email" required autocomplete="email" aria-describedby="email-hint email-error"><br> <output id="email-hint" class="hint" for="email">We send one confirmation, nothing else.</output><br> <output id="email-error" class="error" for="email" aria-live="polite"></output><br> </form-field> <button type="submit">Sign up</button> </form>

Read it as the browser will. The form posts to /api/echo. It has one control that sends data, named email, which must be filled in and must look like an address. It has one button, which submits. When the person presses it, one pair goes out: email= followed by what they typed. The label, the hint, the error and the custom element around them are for the person and never leave the page.

3. The form element

The <form> element's attributes are the instructions for the request. Most forms need only the first two.

AttributeWhat it doesWhen left off
actionThe URL the request goes to, relative or absolute. It may point at another site.The URL of the page the form is on.
methodget puts the data in the URL's query string; post puts it in the request body; dialog sends nothing and closes the dialog the form is in (section 9).get. Any other value, such as put or delete, also means get.
enctypeHow a POST body is encoded: application/x-www-form-urlencoded, multipart/form-data or text/plain (section 7). Ignored for GET.application/x-www-form-urlencoded.
targetWhere the response is shown: _self, _blank for a new tab, _parent, _top, or the name of an <iframe>._self: the response replaces the page.
novalidateSkips the browser's constraint checks on submit. The submit event still fires.Constraints are checked.
autocompleteThe default for autofill across the form's controls. Turning it off usually backfires; Form Accessibility and Usability explains.on.
accept-charsetThe character encoding for the submitted data. The standard now allows only UTF-8.The page's own encoding, which should be UTF-8 anyway.

Two of these defaults catch people. A form with no method sends its data in the URL, which is wrong for anything private, so write the method every time, even when it is get. And HTML forms know only GET and POST: method="put" is not an error the browser reports; it is quietly a GET. Servers that want PUT or DELETE from a plain form usually accept a POST with a hidden field such as _method that names the real intent, a convention the film shows.

An action that already has a query string loses it on a GET submission. The browser replaces the whole query with the form's data, so action="/trails/?sort=new" submits to /trails/?difficulty=easy, not ?sort=new&difficulty=easy. To send a fixed parameter with a GET form, put it in a hidden field. A fragment, the part after #, survives.

4. What happens when you press the button

The HTML standard defines form submission as an exact algorithm. Reduced to its seven main steps, it runs like this:

  1. Find the submitter. The submitter is the button that started the submission. If the person pressed Enter in a text box, it is the form's first submit button. If a script started it, it is whatever button the script named, or the form itself.
  2. Validate. Unless the form has novalidate or the submitter has formnovalidate, the browser runs constraint validation: it checks every control's constraints (required, type, pattern, min, maxlength and the rest). If any fail, it stops, focuses the first problem and shows a message. Nothing is sent.
  3. Fire the submit event. Scripts listening on the form can read it and cancel it with preventDefault(), usually to send the data themselves.
  4. Build the entry list. The entry list is the list of name and value pairs to send, collected from the form's controls in document order. Section 5 covers exactly which controls make it in. As the list is built, the browser fires a formdata event, the last chance for a script to add or change an entry.
  5. Serialize. The entry list is encoded as the enctype says, or into a query string for GET.
  6. Build the request. The method and the action fill the first line; the encoding fills the Content-Type header; the serialized data becomes the query string or the body.
  7. Navigate. The browser sends the request and shows the response in the target: the current page, a new tab, or a frame. Back, reload and history all work, because this is an ordinary navigation.
The seven steps of a form submissionSeven boxes in two rows. Top row, left to right: 1 submitter, the button pressed; 2 validate, constraints checked; 3 submit event, a script may cancel; 4 entry list, named pairs collected. An arrow goes down to the bottom row, which runs right to left: 5 serialize, by enctype; 6 request, method and action; 7 navigate, response to target. Under step 2: invalid, stop here. Under step 3: canceled, stop here. A note at the bottom: form.submit() skips steps 2 and 3; requestSubmit() runs all seven.1 submitterthe button pressed2 validateconstraints checked3 submit eventa script may cancel4 entry listnamed pairs collectedinvalid: stop herecanceled: stop here5 serializeby enctype6 requestmethod + action7 navigateresponse to targetform.submit() skips steps 2 and 3; form.requestSubmit() runs all seven
One submission, in order. Validation and the submit event come first and can each stop it; only then does the browser collect, encode and send the data.

The order explains several things that otherwise look like bugs. The entry list is built after the submit event, so a script that changes a field's value in its submit handler changes what is sent. A form whose fields are invalid never fires submit at all, so a submit handler is not the place to discover that a required field is empty; the browser's invalid event is. And a submission attempt marks every field as touched, so after a failed submit, CSS's :user-invalid matches every field that is still wrong, not only the ones the person visited.

Enter submits

Pressing Enter in a single-line text box submits the form. The standard calls this implicit submission, and it works by clicking the form's default button, the first submit button in the form's source order. Two consequences follow. The first button's name and value are sent, as if it had been pressed, so put the primary action first. And if that first button is disabled, Enter does nothing. A form with no submit button at all still submits on Enter, but only if it has a single text-like field; with two or more, Enter does nothing, which is one more reason every form should have a real button.

Submitting from a script: requestSubmit(), not submit()

A script can start a submission too, and there are two methods for it that look interchangeable and are not. form.submit() is the old one. The standard says it submits the form "bypassing interactive constraint validation and without firing a submit event": it skips steps 2 and 3 entirely. A required field left empty is sent empty, and every submit listener on the page, including the one that was going to send the data with fetch, is silently passed over.

document.querySelector('#report').submit();

form.requestSubmit() does what a button press does: it validates, fires submit, and either can stop it. Pass it a submit button and that button becomes the submitter, its name and value are sent, and its override attributes from section 8 apply. It throws a TypeError if the argument is not a submit button and a NotFoundError if the button belongs to another form. It is Baseline widely available, supported across browsers since September 2022.

const form = document.querySelector('#report'); const send = form.querySelector('button[value="send"]'); form.requestSubmit(send);

Older tutorials, including earlier versions of this course's notes, describe submit() as running validation and firing the event. It does not. Use requestSubmit(). Script that takes over submission entirely, listening for submit and sending the data itself, is covered in Events: forms.

5. What gets sent, and what does not

Step 4 walks the form's controls in document order and decides, control by control, whether each one contributes a pair. The rules are short and every one of them surprises someone:

The controlWhat is sent
No name, or name=""Nothing. Not an empty value: nothing at all. An id does not count.
disabled, or inside a disabled <fieldset>Nothing.
readonlyIts value. The person cannot change it, but it is sent. It is also skipped by constraint validation.
type="hidden"Its value. "Hidden" means not drawn; it is in the source and in the request for anyone to read or change.
Unchecked checkbox or radio buttonNothing. The server learns "no" only from the key being absent.
Checked checkbox with no valuename=on.
<select multiple> with two options chosenThe name twice: hazards=mud&hazards=fallen-tree. A disabled option is never sent.
<textarea> with a line breakThe break as CR LF, %0D%0A, whatever the person's system uses.
A buttonIts name and value, but only if it is the submitter. Every other button is skipped.
<input type="image" name="pin">Where it was clicked, pin.x=30&pin.y=12, if it is the submitter. From the keyboard, pin.x=0&pin.y=0.
<input type="file">The file's contents with multipart/form-data; only its file name otherwise (section 7).
<output>, <label>, any textNothing. Only controls send data.
Which controls make it into the entry listOn the left, nine controls in source order: input name hiker; input with an id of nickname and no name; input name permit, readonly; input name coupon, disabled; hidden input name source; select name hazards, multiple; checkbox name news, unchecked; checkbox name map, checked, with no value; button name action, value send. An arrow leads to a green box, sent in source order: hiker equals Joe plus O percent 27 Reilly, permit equals TN-2048, source equals trailhead-sign, hazards equals mud, hazards equals fallen-tree, map equals on, action equals send if pressed. Another arrow leads to a red box, not sent: nickname with no name, coupon disabled, news unchecked.<input name="hiker"><input id="nickname"><input name="permit" readonly><input name="coupon" disabled><input type="hidden" name="source"><select name="hazards" multiple><input type="checkbox" name="news"><input type="checkbox" name="map" checked><button name="action" value="send">sent, in source orderhiker=Joe+O%27Reillypermit=TN-2048source=trailhead-signhazards=mudhazards=fallen-treemap=onaction=send(only if pressed)not sentnickname: no namecoupon: disablednews: unchecked
The entry list from the demo form. Readonly and hidden fields are sent; a field with no name, a disabled field and an unchecked box are not. The select sends its name once per chosen option.

Three of these rules do daily damage. The missing name is the most common reason a field "is not arriving" at the server: a control with an id and a label looks finished on screen and sends nothing. The unchecked box means a server cannot tell "unticked" from "this form never had that box"; code that reads a checkbox should treat a missing key as no, and a form that needs an explicit "no" should use two radio buttons. Disabled versus readonly is a choice about the request, not just the look: disable a field to leave it out, make it readonly to show it and send it. Neither one protects anything; anyone can remove the attribute in DevTools, as Form Security shows.

Two smaller rules complete the list. Controls inside a <datalist> are skipped, and a hidden input named _charset_ is filled in by the browser with the encoding it used, a fossil from the days when not everything was UTF-8.

How the pairs are written

The default encoding, application/x-www-form-urlencoded, writes the entry list the way a query string is written: each pair as name=value, pairs joined by &. Characters that would break that format are escaped. A space becomes +; most punctuation and every non-ASCII character becomes a percent sign followed by two hexadecimal digits for each byte of its UTF-8 encoding. Letters, digits and *-._ are left alone.

TypedSent
Joe O'ReillyJoe+O%27Reilly
ford & go slowford+%26+go+slow
1+1=21%2B1%3D2
ana@example.comana%40example.com
PeñasquitosPe%C3%B1asquitos
Two linesmile+2.%0D%0AUse

The escaping is why a value can safely contain & or =: a literal plus sign is sent as %2B, so a + on the wire always means a space. Every server framework decodes this for you. The thing to remember is the reverse: what arrives is always text. A number, a date, a checkbox, all arrive as strings, and the server decides what they mean.

6. GET and POST

The same entry list can travel two ways, and the choice decides who else gets to see it.

Where GET and POST put the dataTwo requests side by side. On the left, GET: the first line reads GET slash search question mark q equals blisters HTTP 1.1, with the query string highlighted inside the first line, followed by a Host header, an Accept header, and no body. Below it: the data travels in the URL, so it lands in the address bar, history and bookmarks, and in server and proxy logs and same-origin Referer headers. On the right, POST: the first line reads POST slash signup HTTP 1.1, then Host and a Content-Type of application x-www-form-urlencoded, then a blank line and the body, email equals ana percent 40 example dot com, highlighted. Below it: the data travels in the body, not in the URL or history, but is still readable without HTTPS.GET: the data is in the URLGET /search?q=blisters HTTP/1.1Host: example.comAccept: text/html,…(no body)POST: the data is in the bodyPOST /signup HTTP/1.1Host: example.comContent-Type: application/x-www-form-urlencodedemail=ana%40example.comThe URL lands in the address bar, history,bookmarks, shared links, server and proxylogs, and same-origin Referer headers.Not in the URL, history or access logs.Still plain text on the wire without HTTPS.Reload asks before sending it again.
GET writes the data into the request's first line, where the URL goes; POST writes it into the body, after the headers.

GET asks a question

With GET, the serialized data becomes the query string of the URL and the request has no body. That makes the result a URL like any other: it can be bookmarked, shared, reloaded and cached, and the back button works without a warning. HTTP calls GET safe: it asks for something and changes nothing, so the browser, a crawler or a prefetcher can repeat it freely. That is exactly right for a search, a filter, a sort or a page of results.

It is exactly wrong for anything private, because a URL is one of the leakiest places on the web. The query string of a GET form ends up in:

  • the address bar, for anyone looking at the screen, and the browser history, for anyone who uses the device later;
  • bookmarks and copied links, which carry it wherever they are pasted;
  • the access logs of the server, and of any proxy, load balancer or analytics service in between, which are kept for months and read by people who never saw the form;
  • the Referer header of requests the results page makes to its own site. Requests to other sites get only the origin by default, under the browser's strict-origin-when-cross-origin referrer policy, unless a page has loosened it.

A password, an email address, a health question or anything typed in confidence must never go out by GET. HTTPS does not help here: it encrypts the URL on the wire, but the history, the logs and the screen all see it in the clear. URLs also have practical length limits; many servers refuse a request line longer than about 8 KB by default, so a long message does not belong in one.

POST makes a change

With POST, the serialized data becomes the request body and the URL stays as the action wrote it. Use it whenever the request does something: creates an account, places an order, sends a message, deletes a report. POST is not safe and not idempotent; sending it twice may do it twice, which is why the browser asks before reloading a page that was the answer to a POST. A well-behaved server answers a successful POST with a redirect, status 303, to an ordinary page, so that reloading that page repeats a harmless GET instead of the order. This is called Post/Redirect/Get.

POST keeps the data out of the URL, and that is all it does. The body is as readable as the URL to anyone on the network unless the connection is HTTPS, and the server and DevTools read it in full. POST is not encryption, and it is not security. Patterns: The form already talks shows both requests arriving at /api/echo, headers and all.

7. Files, and the other encodings

A POST body can be written three ways, chosen with enctype. Patterns: What to send compares them in depth, together with the JSON a script can send; here is what matters for HTML forms.

multipart/form-data, for files

A URL-encoded body is one line of text, and a file is not text. To upload a file, a form needs method="post" and enctype="multipart/form-data". The browser then writes the body as a stack of parts, one per entry, divided by a boundary string it invents and names in the Content-Type header. Each part has its own small headers, and a file's part carries its file name, its type and its bytes, unencoded:

POST /api/echo HTTP/1.1 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary63vFfQFOSNv4ggvV ------WebKitFormBoundary63vFfQFOSNv4ggvV Content-Disposition: form-data; name="hiker" Joe O'Reilly ------WebKitFormBoundary63vFfQFOSNv4ggvV Content-Disposition: form-data; name="photo"; filename="note.txt" Content-Type: text/plain hello trail ------WebKitFormBoundary63vFfQFOSNv4ggvV--

Forget the enctype and nothing fails, which is the trouble. The browser sends photo=note.txt, the file's name and not one byte of the file, and the server receives a perfectly valid form with no upload in it. If no file is chosen, a multipart form still sends the part, with an empty file name and no content. Multipart works for forms without files too; it is simply larger, so the default stays URL-encoded.

text/plain, for almost nothing

The third value writes each pair as name=value on its own line, with no escaping at all. The standard's own description is that such payloads "are not reliably interpretable by computer, as the format is ambiguous": a value that contains a line break or an = cannot be told apart from the separators. It is fine for a human to read in a debugging tool and for nothing else. Do not use it to send data a program must parse.

And no JSON

No enctype sends JSON. An HTML form sends URL-encoded or multipart data, and that is all; JSON needs a script that takes over the submission and builds the request with fetch. A server that accepts only JSON therefore breaks the form for anyone whose script did not run. The film's closing chapters show the progressive answer: a server that reads the Content-Type and accepts both.

8. The button has a say

A form's attributes set its defaults. The button that submits it, the submitter, can add to the data and override every one of those defaults for its own submission.

Buttons with a name and value

A submit button with a name and value adds that pair to the entry list when it is the one pressed, and only then. That lets one form offer several actions and lets the server tell them apart:

<button type="submit" name="action" value="send">Send report</button> <button type="submit" name="action" value="draft" formnovalidate>Save draft</button>

Press the first and the server receives action=send; press the second and it receives action=draft. With <button>, the value is separate from the visible text, so the server's key does not change when the label is reworded or translated. An <input type="submit"> works too, but its value is also its label, so the server ends up matching on display text.

Overriding the form

Five attributes on a submit button replace the form's own settings, for that button's submission only:

On the buttonReplacesTypical use
formactionaction"Delete" posting to a different URL from "Save."
formmethodmethodA "Preview" that sends the same fields by GET; a "Cancel" with formmethod="dialog".
formenctypeenctypeOne button that sends as multipart, as the demo's does.
formtargettargetA "Print version" that opens in a new tab.
formnovalidateValidation"Save draft" for a half-finished form.

They work on <button type="submit">, <input type="submit"> and <input type="image">, and they apply when that button is the submitter, whether it was clicked, reached by Enter as the default button, or passed to requestSubmit(). One form can therefore write quite different requests:

<form action="/reports" method="post"> … <button type="submit" name="action" value="send">Send</button> <button type="submit" name="action" value="preview" formmethod="get" formaction="/reports/preview" formtarget="_blank">Preview</button> <button type="submit" name="action" value="delete" formaction="/reports/delete" formnovalidate>Delete</button> </form>

Image buttons

<input type="image"> is a submit button drawn as a picture. It needs an alt, which is its accessible name. Instead of a name and value, it sends where it was clicked, in pixels from the image's top-left corner, as two pairs: pin.x=30&pin.y=12. Activated from the keyboard it sends pin.x=0&pin.y=0. Unless the coordinates really matter, as on a clickable map, a <button> with an <img> inside is simpler, sends a proper value and is styled like every other button.

Know which buttons submit

A <button> with no type inside a form is a submit button. A button meant to open a menu or toggle a panel, written without type="button", submits the form when pressed, and the page reloads before the script runs. Write the type on every button, every time. Form Fields covers the button types, and why type="reset" is best left out.

9. Beyond the form element

Controls outside the form: the form attribute

A control belongs to the form it sits inside. It can also belong to a form somewhere else in the document: give the form an id, and give the control form with that id. The control then takes part in the submission like any other: it is validated, its pair is added to the entry list, and a submit button outside the form submits it. Forms cannot be nested, so this is also how one control can serve a form while sitting inside the layout of another part of the page.

<form id="permit" action="/api/echo" method="post"> … </form> <label for="trail">Trail</label> <input id="trail" name="trail" type="text" form="permit"> <button type="submit" form="permit">Request permit</button>

Entries are collected in document order, so trail arrives after everything inside the form. The attribute works on every form control and on <fieldset> and <output>. It is an escape hatch: the source no longer shows what the form contains, so use it only when the layout leaves no better choice, as Organizing Forms advises.

method="dialog": a form that sends nothing

Inside a <dialog> element, a form with method="dialog" does not make a request. Submitting it closes the dialog, and the submitter's value becomes the dialog's returnValue, which a script reads in the dialog's close event. That makes a confirmation box a few lines of markup:

<dialog id="confirm-delete"> <form method="dialog"> <p>Delete this trail report?</p> <button type="submit" value="keep">Keep it</button> <button type="submit" value="delete">Delete</button> </form> </dialog>

The rest of the lifecycle still runs first: constraints are checked and submit fires, so a required field inside a dialog blocks its Cancel button too. The usual fix is a Cancel button with formmethod="dialog" and formnovalidate inside an ordinary POST form, so that Save sends the data and Cancel just closes the box. Nothing in the form is sent and nothing is cleared; the fields keep their values for the next time the dialog opens. A form with method="dialog" that is not inside an open dialog does nothing at all.

10. One form, every surprise

The demo is a trail permit request with every rule from this page in it: a field with an id and no name, a readonly permit number, a disabled discount code, a hidden source, a select with two hazards chosen, notes with a line break and an ampersand, three checkboxes, a file input, and a trail field written after the form and joined to it with the form attribute. Every field is already filled in. Four submitters send it four ways, each to /api/echo in a new tab:

  • Send (POST) submits as the form says: URL-encoded, in the body, with action=send.
  • Preview (GET) has formmethod="get": the same pairs move into the address bar, and the body is empty.
  • Send as multipart has formenctype="multipart/form-data": each pair becomes a part, and a chosen file arrives whole.
  • Map pin is an image button: in place of action, it sends pin.x and pin.y.

Pressing Send produces this body, one line in the echo, broken here at each pair:

hiker=Joe+O%27Reilly &permit=TN-2048 &source=trailhead-sign &hazards=mud &hazards=fallen-tree &notes=Bridge+out+at+mile+2.%0D%0AUse+the+ford+%26+go+slow. &share=yes &map=on &photo= &action=send &trail=Lake+Loop

No nickname, no coupon, no news. The readonly permit and the hidden source are there. The file input with no file chosen sends an empty photo=, and trail comes last because it comes last in the document.

11. Summary

ConceptKey points
A parameterized linkA GET form is a link whose query string the person fills in; a POST form writes a request with a body. Both work with no script.
The piecesThe form (where and how), controls (name for the server, id for the page), labels and fieldsets (names for people), buttons (submit unless type="button"), and <output> messages that are never sent.
Form attributesaction defaults to the page's URL, method to GET, and any method other than get, post or dialog means GET. GET replaces the action's query string. accept-charset may only be UTF-8.
LifecycleSubmitter, validation, submit event, entry list, serialization, request, navigation. Enter clicks the first submit button. requestSubmit() runs every step; submit() skips validation and the event.
The entry listNo name, disabled, unchecked: not sent. Readonly and hidden: sent. A checkbox with no value sends on; select multiple repeats the name; line breaks become CR LF; only the submitter's name and value go; image buttons send .x and .y. Everything arrives as text.
GET and POSTGET for questions: the data is in the URL, so it is in history, bookmarks, logs and same-origin Referer. POST for changes: the data is in the body, still readable without HTTPS. Answer a POST with a 303 redirect.
EncodingsURL-encoded by default; multipart/form-data for files, without which only the file name is sent; text/plain is ambiguous and for people only. Forms cannot send JSON.
The submitterA button's name and value tell the server which action was chosen. formaction, formmethod, formenctype, formtarget and formnovalidate override the form for that button.
Beyond the elementThe form attribute joins a control outside the form; it is sent in document order. method="dialog" closes a dialog, sets its returnValue and sends nothing, after validation runs.