HTML Forms: How (and Why) to Prevent Double Form Submissions

When double clicking a submit button your form will be sent twice. Using JavaScript you can prevent this from happening, but wouldn’t it be nice if this behavior could be tweaked by use of an attribute on the <form>? If you think so, feel free to give this issue a thumbs up.

Today Sebastian wondered:

I quickly chimed in saying that I do tend to lock up forms when submitting them. Let me explain why …

~

I started locking up submitted forms as users of the apps I’m building reported that sometimes the actions they had taken — such as adding an entry — were performed twice. I took me some time to figure out what exactly was going on, but eventually I found out this was because they were double clicking the submit button of the forms. As they double clicked the button, the form got sent over twice. By locking up forms after their first submission, all successive submissions — such as those triggered by that second click of a double click — are ignored.

~

To prevent these extra form submissions from being made I don’t hijack the form with Ajax nor do I perform any other complicated things. I let the inner workings of the web and forms in the browser be: when pressing the submit button the browser will still collect all form data, build a new HTTP request, and execute that request.

What I simply do is extend the form’s capabilities by adding a flag — by means of a CSS class — onto the form to indicate whether it’s being submitted or not. I can then use this flag’s presence to deny any successive submissions, and also hook some CSS styles on. — Progressive Enhancement, Yay! 🎉

The code looks as follows:

 // Prevent Double Submits
document.querySelectorAll('form').forEach(form => {
	form.addEventListener('submit', (e) => {
		// Prevent if already submitting
		if (form.classList.contains('is-submitting')) {
			e.preventDefault();
		}
		
		// Add class to hook our visual indicator on
		form.classList.add('is-submitting');
	});
});

💡 Although the problem initially was a double click problem, we don’t listen for any clicks on the submit button but listen for the form’s submit event. This way our code not only works when clicking any of the submit buttons, but also when pressing enter to submit.

With that .is-submitting class in place, we can then attach some extra CSS onto the form to give the user visual feedback. Here’s a few examples:

See the Pen
Prevent Form Double Submits
by Bramus (@bramus)
on CodePen.

See the Pen
Prevent Form Double Submits (Alternative version)
by Bramus (@bramus)
on CodePen.

Note that this solution might not cover 100% of all possible scenarios, as it doesn’t take failing networks and other things that might go wrong into account. However, as I’m relying on the basic mechanisms of the web I think I can also rely on the browser to show that typical “Confirm Form Resubmission” interstitial should a timeout occur. Additionally, if need be, one could always unlock the form after a fixed amount of time. That way the user will be able to re-submit it again.

~

Dealing with double form submissions isn’t a new issue at all. You’ll find quite some results when running a few queries through Google — something I only found out after stumbling over the issue myself.

Back in 2015 (!) Mattias Geniar also pondered about this, after being pulled into the subject from a sysadmin view. Now, when even sysadmins are talking about an HTML/UX issue you know there’s something going on. This made me wonder why browsers behaved like that and how we could solve it:

As a result I decided to open an issue at the WHATWG HTML Standard repo, suggesting for a way to fix this at spec level:

An attribute on <form> to tweak this behavior – instead of having to rely on JavaScript – would come in handy and form a nice addition to the spec.

I see two options to go forward:

  1. Browsers/the standard keeps the current behavior and allow multiple submits. Developers must opt-in to prevent multiple submissions using a preventmultiplesubmits attribute.
  2. Browsers/the standard adjust the current behavior to only submit forms once. Developers must opt-in to allow multiple submissions using a allowmultiplesubmits attribute.

Initial response on the issue was very low, and it looks like this isn’t that big of a priority.

Back then I was more in favor of the second solution, but now I’m quite undecided as changing the default behavior — which has been around for ages — is quite tricky.

~

Another way that this issue could be fixed is at the browser level: if they were to treat double clicks on form submit buttons as single clicks, then the double click problem — but not the double submit problem — could also be taken care of.

To then attach styles to forms being submitted a CSS Pseudo Class :submitting would come in handy. Come to think of it, this Pseudo Class would be a quite nice addition to CSS in itself, no matter whether this double click issue gets solved or not.

~

Winging back to the addition to the HTML spec I suggested: If you do think it could be something the HTML spec could benefit from, feel free to add a thumbs up to the issue to indicate that you want this, or add an extra comment to it if you have more input on the subject.

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Serverless functions with Vercel

Have a static site but looking to add backendy stuff — such as subscribing to a newsletter — to it? Geoffrey Dhuyvetters from madewithlove walks us trough setting up a serverless function with Vercel (formerly known as Zeit):

In this article I’ll walk you through the steps to create serverless functions with Vercel. These functions will provide you with a place to add server side logic like verifying captcha’s, sending emails or liking posts that you can use in your static sites.

Serverless functions with Vercel →

Make <input type="number"> respond to arrow keys with modifier keys

By default <input type="number"> elements will increment/decrement by its step attribute value when pressing the up/down arrows. Kilian Valkhof provides us with some JavaScript to have these elements also respond to up/down keypresses while holding modifier keys.

When someone uses the arrow keys in the input field, we want the following to happen:

  • If they press up or down, we want to add or subtract 1
  • If they hold SHIFT and press up or down, we want to add or subtract 10
  • If they hold ALT and press up or down, we want to add or subtract 0.1
  • If they hold CTRL and press up or down, we want to add or subtract 100. On Mac, we want to use the CMD key for consistency.

Supercharging <input type="number">

💡 Did you know the DevTools in your browser also support these modifier keys? Try editing a numeric value and press up/down while holding SHIFT/ALT/CMD 😉

⚠️ In some cases you’ll most likely be better off by avoiding <input type="number">, and should use go for <input type="text" inputmode="numeric" pattern="[0-9]*"> instead`.

Go Make Things: Service Workers

Chris Ferdinandi has been writing an excellent post series on Service Workers. After first explaining what Service Workers are and how to create one, he — as per usual on Go Make Things — also provides us with practical scenarios such as making pages available offline, caching web fonts to improve rendering, etc.

Go Make Things: Service Workers →

☝️ You should really follow Chris’s blog as his Daily Developer Tips — in which this Service Workers series appeared — are always very good.

Say No To Complexity With AlpineJS

If you’re looking for a quick rundown of the aforementioned AlpineJS, here’s a talk by AlpineJS’s author Caleb Porzio which he presented at JSNation:

A Lightweight Masonry Solution

With Masonry being specced in Grid Layout Module Level 2 – and already being implemented in Firefox – Ana Tudor looked into whipping up a lightweight fallback for browsers that don’t support it.

🤔 Masonry Layout?

Masonry is a grid layout based on columns, as popularized by Pinterest. Unlike other grid layouts, it doesn’t have fixed height rows. It works by placing elements in optimal position based on available vertical space, sort of like a mason fitting stones in a wall.

It became easy to implement thanks to the Masonry JavaScript Library.

After first laying out all images in columns, she then calculates the vertical gaps between all items. Each gap is adjusted – using a negative top margin – so that the items shift upwards, after which them end up at their wanted positions.

A Lightweight Masonry Solution →

Refactoring optional chaining into a large codebase: lessons learned

Lea Verou on how Optional Chaining affected her code, along with caveats and scenarios where it couldn’t be used.

Now that optional chaining is supported across the board, I decided to finally refactor Mavo to use it. This is a moment I have been waiting for a long time, as I think optional chaining is the single most substantial JS syntax improvement since arrow functions and template strings.

Refactoring optional chaining into a large codebase: lessons learned →

React v17.0: A “Stepping Stone” Release

No new features, but a lot of changes, paving the way for future releases.

The React 17 release is unusual because it doesn’t add any new developer-facing features. Instead, this release is primarily focused on making it easier to upgrade React itself.

We’re actively working on the new React features, but they’re not a part of this release. The React 17 release is a key part of our strategy to roll them out without leaving anyone behind.

In particular, React 17 is a “stepping stone” release that makes it safer to embed a tree managed by one version of React inside a tree managed by a different version of React.

Here are the major changes:

  1. Changes to Event Delegation
  2. No Event Pooling
  3. Effect Cleanup Timing Change
  4. Consistent Errors for Returning undefined
  5. Methods of removing private exports

React v17.0 Release Candidate: No New Features →

Using JavaScript’s closest() Method to Capture a “Click outside” an Element

In Practical Use Cases for JavaScript’s closest() Method, Andreas Remdt talks about some nice use cases that use Element.closest().

I especially like this example with a menu. Click on one of the links and it will show the menu which has the class menu-dropdown. Clicking outside said menu will close it. It’s that latter one that leverages Element.closest().

var menu = document.querySelector(".menu-dropdown");

function handleClick(evt) {
  // Only if a click on a dropdown trigger happens, either close or open it.
  …
  
  // If a click happens somewhere outside .menu-dropdown, close it.
  if (!evt.target.closest(".menu-dropdown")) {
    menu.classList.add("is-hidden");
  }
}

window.addEventListener("click", handleClick);

Here’s a pen with the result:

🔥 If you haven’t checked out Hakim El Hattab’s dynamically drawn hit areas for menus, as talked about in Building Better Interfaces, be sure to do so, as they’re amazing:

Use your Smartphone to move a pointer on a remote page with JavaScript

Sanjeet Chatterjee built this nice demo where your smartphone can act as a virtual pointer for a webpage.

With a fundamental shift to the web, we are able to do really cool things right from our browser. In this tutorial, we will be utilising the Generic Sensor API to turn your smartphone into a pointer with real-time tracking.

Just like you use a Wiimote (Wii Controller), you can tilt/move your phone to move the dot on the page:

The “quaternions” provided by the AbsoluteOrientationSensor API are converted to yaw and roll, which are then transferred to the canvas-page using websockets.

Unfortunately the demo doesn’t work on iOS (MobileSafari) or Firefox, as those browsers do not support AbsoluteOrientationSensor:

Data on support for the mdn-api__AbsoluteOrientationSensor feature across the major browsers

💡 Shown above is an interactive CanIUse.com embed, showing an always up-to-date support table. By the time you are reading this browser support might have become better.

For those browsers, a fallback using the deviceorientation event could be implemented.

Track Your Smartphone in 2D With JavaScript (Article) →
PhoneTrackingDemo Canvas Page →
PhoneTrackingDemo Controller →
PhoneTrackingDemo Source (repl.it) →

💵 This linked article is stuck behind Medium's metered paywall, which may prevent you from reading it. Open the link in an incognito window to bypass Medium's ridiculous reading limit.

☝️ If you lack an Android Smartphone with a modern Chrome on it, you can test it all out using Chrome on Desktop: Open the controller page in Chrome and use the DevTools’ Sensors panel to emulate/override the position of the device.