Problem

I found that if users double-click the submit button (using the mouse or the “Enter” key on the keyboard), it will trigger the form submission once again.

As a result, the same form is received multiple times.

This can happen in regular form submissions (using the POST method for example) or in forms that use ajax or fetch.

Moreover, this issue can be common if there is no visual indication that the form is being submitted (like a loading animation). In this case, users may think the submission hasn’t started and will therefore click the submit button again.

Solution

We would obviously want to avoid submitting the same form multiple times.

This can be done by disabling the submit button ("disabled" attribute) while the form is being submitted. This way, even if the user clicks the button again while the submission is still in progress, it will not trigger another submission.

So I created this simple submissionMode JS function to use in my fetch functions:

/**
 * Submission Mode.
 *
 * Handles processing time when submitting a form.
 *
 * @link https://notesontech.com/preventing-double-form-submission/
 *
 * @param {string} action - "start" or "stop".
 * @param {HTMLElement} element - The submit button or the container of the button.
 */
function submissionMode(action = 'start', element) {
	if (typeof element === undefined || element === null) {
		return;
	}

	// If this is a jQuery element, convert it to vanilla JS.
	if (element instanceof jQuery) {
		element = element[0];
	}

	let submitButton;

	if ('BUTTON' === element.tagName && 'submit' === element.type) {
		// The provided element is the submit button.
		submitButton = element;
	} else {
		// The provided element is the container.
		// Search for a submit button within this container.
		submitButton = element.querySelector('button[type="submit"]');
	}

	if (submitButton !== null) {
		if ('start' === action) {
			submitButton.classList.add('loading');
			submitButton.disabled = true;
		} else if ('stop' === action) {
			submitButton.classList.remove('loading');
			submitButton.disabled = false;
		}
	}
}

This function supports two parameters:

  • action: which can be either start or stop.
  • element: which is the submit button we want to disable/enable, or the container where this button exists.

If we’ll use the start action, it will disable the submit button (button[type="submit"]) and add a loading class to it.

If we’ll use the stop action, it will reactivate the button and remove the loading class. This can be used, for instance, to allow another (and new) form to be submitted if needed.

Using CSS, we can create a new .loading class and change the design of the button to provide a clear indication of loading. This can include color changes or a loading animation.

Flexibility of the element parameter

The element parameter can be either the submit button itself or the container (like the form element) where the submit button is located.

If we pass the container element, the function will automatically use the first submit button (button[type="submit"]) within this container.

Another feature is the ability to pass to this function both vanilla JS and jQuery elements. In case a jQuery element is provided, the function will convert it to vanilla JS (read more in my other post: Converting jQuery object to vanilla JS).

How to use

First, we’ll need to place this function in a JS file that is loaded on all relevant pages (such as pages that have a form).

Then, we’ll add the following to the beginning of all fetch functions:

submissionMode('start', form);

This start action will start the submission mode and prevent double form submission.

We’ll add the following to stop the submission mode and reactivate the submit button:

submissionMode('stop', form);

This stop action can be used multiple times within the fetch function. For example, after the submission is complete or in case the form validation fails.

As explained, we can pass the submit button itself as the second parameter, instead of the form element.