Did you ever create a HTML form, just to discover that it doesn't send what and where you want?

That the data you send cannot be properly used or that you don't get what you expected?

When you cobble together a form, but never understand the real meaning of all the different enctypes and methods, you will always just be able to only assume what trouble you will run into next.

If you are like me and found out the hard way, join me in this article to master the beast of forms. Learn how exactly to ask for the right data or submit whatever is entered.

Table of Contents

Forms are just tiny wrappers around HTTP

If HTML and CSS is all you use to create your forms, you will soon discover the border of what is possible and impossible.

Why are forms so exhausting? It is not the HTML that is exceptionally hard or tough to write and figure out. It is the deceptive way how form attributes make you think, you can build forms with just raw HTML and some CSS sprinkled in.

If you are knowledgable enough, you know what else is required:

  1. A server-side piece of code that processes the request via HTTP.
  2. Make the experience seamless and without reloading the page once - using JavaScript.

Piecing all that together is tiresome.

But you are not alone!

After reading this article, you will be able to answer these questions:

  • What enctype do you use, when the server only speaks application/json?
  • What method do you use, when the <form> element only knows about GET and POST but you actually need to DELETE a post?
  • What action do you use, when you want to submit data without wanting to reload the page?
  • How do I send two requests with one form?

A Game of Faking

There are two major rules when it comes to the properties enctype and method.

First:enctype only knows three types:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Second:method only knows about GET and POST.

The enctype corresponds to the HTTP request header content-type.

GET and POST conventions

When you use GET in a form, it is sent via query parameters (?username=test) and using text/html.

If you set the action to POST, the request is automatically sent as application/x-www-form-urlencoded and the data is put into the body of the request. But that doesn't mean query parameters do not exist in a POST request. In fact, you can have both query parameters and a body in a request. Usually you have only one of the two.

PHP makes this super confusing by populating both $_GET and $_POST in that case.

Additionally, I was pretty surprised that GET requests allow a body too. It is all just a question of convention and what fields of a request the server responds to.

Luckily, most webservers follow the same conventions. But be prepared for the wild wild web where nobody has to obey any convention and potentially run a webserver on their toaster.

GET is generally avoided for submitting data, because the inputs are saved to your browser history and are cached. You would want to avoid that users can easily submit a form, just by following a URL.

However, GET is useful for asking for data, and this is where the boarders blur. You need to put data into your request, such as what data to ask for. This can be basically in any part of the URL or request. It is just convention where to put them.

Anatomy of a request:

  • Query Parameters: filters, sorting, order and pagination - everything that changes the way how the requested page is displayed
  • Body: complex structured or unstructured data - user data submissions
  • Headers: meta data - usually only read by the browser and the server
  • Method: the way how the request should be processed by the server - create, read, update, delete
  • URL: single view, page or collection of data to request

The next scenarios will show you when these attributes of a <form> actually do not play a role.

Dynamic Submission - When you don't want to reload

In principle you only use method and action if you want a pageload. That means reloading the same page or redirecting to another page.

As soon as you build a form that need its contents processed without page reloads, you will not get anywhere with <form> attributes. But a form can still be used to get the accessibility bonus for free. That means the input can react on the ENTER key or a submit button. Multiple inputs are grouped together. It is less effort javascript-side to re-implement all this normal browser behavior.

An example is a search input that shows quick search results. It may look like this:

This is a form that dynamically renders the results of the query when requested to. Using javascript, we can react upon the input event of the text input.

While the underlying mechanism to get the entries doesn't necessarily need to rely on data being requested from a server, we still use a form.

In case you are curious...

In the screenshot above, I use Vuepress. Here the list of all pages is already there as a JSON array of page objects. This way, no server request is needed to display results.

Why?

Additionally, we can we hook into the submit event of the form and preventDefault(). Then display results in a relative positioned container.

To push it a step further, you could have the page actually handle a pageload, if javascript is disabled, and then show the requested list. This is a perfect example of progressive enhancement.

Submitting another format

Say your API speaks JSON, do you set the enctype to application/json? Sorry, that does not work out of the box. If you want to submit your data in any other format, you can forget about setting enctype alltogether.

Once you use JSON or any other format, setting up an AJAX call becomes mandatory. That is because the encode type defines how the formdata is serialized. Natively, the browser only knows about the previously mentioned three types: plaintext, formdata and urlencoded.

There were plans to implement JSON as enctype for forms, but this was unfortunately abandoned.

Fortunately, you can convert a form to JSON very easily using FormData:

const formElement = document.querySelector('.form')
Object.fromEntries(new FormData(formElement).entries())

This works for simple transformation. Since forms are string-based, you will get almost semantically valid data. So if you select a radio input. You don't get false or true but "on" and "off".

Below you can see how that looks in action. I added a bit more magic to convert "on" to true and "off" to false.

Dog Cat Rabbit
{
  "email": "mydog@emyhome.work",
  "specie": "Dog",
  "amount": 20,
  "food": false
}

Below you can compare the other regular enctypes that you can send with forms by using the enctype property.

text/plain

tShirtSize=L
tShirt=on

application/x-www-form-urlencoded

tShirtSize=L&tShirt=on

multipart/form-data

------WebKitFormBoundary3cBoF3eHzPSATbPz
Content-Disposition: form-data; name="tShirtSize"

L
------WebKitFormBoundary3cBoF3eHzPSATbPz
Content-Disposition: form-data; name="tShirt"

on
------WebKitFormBoundary3cBoF3eHzPSATbPz--

multipart/form-data is so exhaustive, because you can also send binary data with it.

Of course - you need also a server that can do something with the data, store it to a database or consume it in some other way.

Make good use of form properties - configure AJAX via HTML

We could use the action attribute to author the URL where data goes to. We already store in the HTML what data we want to submit with form fields, so why not where it goes to?

Even if it is only processed via javascript. This way the target URL can be configured in the form itself.

<form action="/api/users">...</form>
const form = document.querySelector('.form')
form.addEventListener('submit', event => {
    let credentials = {
        user: 'cueball',
        password: 'correcthorsebatterystaple'
    }
    fetch(event.target.action, {
        body: JSON.stringify(credentials),
        type: 'POST',
    })
})

Conclusion

Forms and their associated submission logic are tightly connected to the capabilities of the server. As soon as you need something extra, you are mostly set out to solve it all client-side or chnage your server. Depending on what is in your reach, there is always a "quick and working but incorrect" solution and a "working and correct" solution.

How do you charter unknown server territory?

Don't get stuck connecting forms and servers.
Get free on-spot advice delivered to your inbox.

Join the list