A return to form: crafting a good form experience without JavaScript

Squeeds Julkalender | 2019-12-07 | Tobias Ljungström
When all you have is a hammer every problem looks like a nail. But there are many more things in the web dev toolbox.

Using only HTML and CSS we can create a fully functional form, without a single line of JavaScript. And seeing as how a form is one of the primary means of user interaction with websites, using the methods described below has the potential to significantly reduce the amount of JavaScript on the web. Anyway, let's get to it.

The form element

It all starts with the form element. This is where all of your input fields should live. It is not only good semantics, but crucial for all the other parts of the form creation (though it is possible to have input elements belong to a form even if they live outside the form element).

    <label for="input_id">Name:</label>
    <input type="text" id="input_id" name="input_name">

    <label for="textarea_id">Message:</label>
    <textarea id="textarea_id" name="textarea_name"></textarea>

  <div class="button">

Now we have a basic form markup with a couple of fields the user can fill in and a button to click. It's time to add some functionality. I'll go through each of the things we want to add step by step.

Submitting the form

At this point you might think its time to ad an onClick-handler to the button and the execute some JavaScript that looks at the form and then makes a request to the backend.


All you have to do to make submitting work is add the action and method attributes to the form tag, and make the button have the type submit. Then when the button is pressed the form data will automatically be collected and sent to the url specified in the action attribute, with the request type specified in the method attribute. Like so:

<form action="/a-nice-endpoint" method="post">

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

This will, when the button is pressed, make a POST request to the url /a-nice-endpoint with two pieces of data, keyed by the fields respective names.

Clear changes

This is something that is usually taught in the first day of web design class, but easily forgotten. Put a button with the type set to "reset" and voila: it will reset the form to all its initial values.

<form action="/a-nice-endpoint" method="post">
  <button type="reset">Clear all changes</button>

Omit fields from the form

Set an input element to be disabled and its value will not be sent along with the form data to the backend. It will also not be interactable.

<input type="text" disabled/>

Focus the first element

Set the "autofocus" attribute on an input field and it will be focused when the page has loaded. Only one element per page can have this, since it is not possible to focus two things at once in a browser.

<input type="text" autofocus/>

Restrict input values

Number inputs can be restriced with a minimum and maximum value that is allowed to be entered. Text inputs can be restricted in how many characters are allowed.

<input type="number" min="0" max="9001"/>

<input type="text" maxlength="144"/>

Dropdown with search field

Ok, so surely this is the time where we have to get our JavaScript out. We have to render a custom built list with all the options, and then respond to the input change...


While it is true that at the time of writing the options for styling a dropdown with CSS is limited, the functionality is all there in the HTML. The trick is in using the datalist element.

  <option>Daft Punk</option>
  <option>The Chemical Brothers<option>

This creates a fully functional and very performant dropdown list with a search field for finding the option you want. It works great even for very large lists, a case where a custom build solution, with one or more separate dom elements for every option, often struggles. In the end it comes down to what you find important with these kind of lists. If you think it worth it to spend many, many hours building a performant, accessible, usable solution (or surrender control by using a third-party option), then you will be able to build something that looks just the way you want it. But if you forego the styling you will be done in a few minutes, and it will work perfectly from the start.

Choosing a date

Now we all know that anything relating to time and date is one of the hardest things to build, but we must build it. Or...?

No. You know how it goes by now. Setting an input field to be of the type "date" or "time" will use the built in picker of the browser. Though since the support for this is still not the deepest, I can understand if you need a different solution for this.

<input type="date"/>

Choosing a color

There's an input field of type "color". It returns a hexadecimal code. Use it.

<input type="color"/>

Validating the form data

This is a big one, but also something that can be done completely without JavaScript, unless you have very specific requirements for your validation, in which case there's a validation API that you should make use of, instead of doing all the heavy lifting yourself.

First of all, setting the "required"-attribute on an input will prevent the form from submitting if the input does not contain any data. It will also automatically display an error message on the fields that were invalid. This message can be customized, but that does require a line of JavaScript.

If you need more advanced validation, such as a text needing to be in a certain format, the "pattern" attribute allows for entering a regular expression that the element will use when validating.

All input elements that make use of this validation receive pseudo-attributes that can be matched with in your CSS in order to style the field accordingly, and provide some nice visual feedback to the user.

<form action="/a-nice-endpoint" method="post">
  <input type="text" required pattern="^[a-zA-Z0-9]$">



A lot of what I've described in this article may seem basic - and it is. But it's important to know about it and remember the tools we have at hand. It is very easy to overcomplicate things when we develop web sites or applications, and we often forget about the standards and build-in functionalities and instead build complex, customized systems ourselves. Not only does this give us more complexity to handle and maintain over time, but it is also easy to forget about corner cases and good practices that the standards have already covered.

Take a look at the pages on the Mozilla Developer Network about forms to learn even more about them, and all that they can do. There's a lot that I haven't mentioned in this article.

I think we sometimes, maybe even often, write too much JavaScript, and we may forget the foundations of the web: HTML and CSS, and all that they can do.

The web is fully functional from the start. Let's not break it.