Skip to main content

Cancelling form submission

A form's default submission is cancelled by calling event.preventDefault() in the submit event handler. After that the browser makes no request to action and does not reload the page, so you handle the data yourself, usually with fetch.

Theory

TL;DR

  • By default submit collects the fields, sends an HTTP request to action and reloads the page.
  • event.preventDefault() inside the submit handler cancels that default behaviour.
  • The form values are easiest to read with new FormData(form) and Object.fromEntries(formData.entries()).
  • The request is then sent manually: fetch() or any HTTP client.
  • Listen to submit on the form, not to click on the button: a form is also submitted by Enter inside a field.
  • The inline onsubmit attribute works too, but it mixes markup with logic and is not recommended.

Quick example

javascript
const form = document.querySelector('#loginForm'); form.addEventListener('submit', (event) => { event.preventDefault(); // cancel the default submission console.log('The form does not reload the page!'); });

The default behaviour of a form

html
<form id="loginForm" action="/login" method="post"> <input name="username" /> <button type="submit">Log in</button> </form>

By default, when the user presses "Log in":

  • the browser collects every field of the form;
  • makes an HTTP request (usually POST) to /login;
  • and reloads the page.

That reload is exactly what breaks any SPA scenario: the application state is lost, and the response cannot be shown without rendering a whole new page.

How to cancel the default submission

Call event.preventDefault() in the submit event handler:

javascript
const form = document.querySelector('#loginForm'); form.addEventListener('submit', (event) => { event.preventDefault(); // cancel the default submission console.log('The form does not reload the page!'); });

Now the browser will not reload, and you can process the data yourself, for example with fetch or any HTTP library.

Example with asynchronous handling

html
<form id="form"> <input name="email" type="email" placeholder="Email" required /> <input name="password" type="password" placeholder="Password" required /> <button>Log in</button> </form> <script> const form = document.querySelector('#form'); form.addEventListener('submit', async (e) => { e.preventDefault(); // cancel the default behaviour const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); console.log('Sending through JS:', data); const res = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); const result = await res.json(); console.log('Server response:', result); }); </script>

What happens here:

  • the form does not reload;
  • the values are taken from the fields through FormData;
  • the request goes out asynchronously through fetch;
  • the result can be shown right on the page.

Alternative: the onsubmit attribute in HTML

html
<form onsubmit="event.preventDefault(); alert('No reload!')"> <input placeholder="Name" /> <button>Send</button> </form>

This works, but it is not recommended: mixing JS and HTML makes the code harder to maintain, prevents reusing the handler and does not play well with a Content Security Policy.

Summary table

What you want to doThe right way
Cancel the page reloadevent.preventDefault() in the submit handler
Read the form datanew FormData(form)
Send the data manuallyfetch() or the HTTP client of your choice
The reliable approachListen to submit, not to click

Just remember: so that the form does not reload the page, always use

javascript
form.addEventListener('submit', (e) => e.preventDefault());

Common mistakes

  • Cancelling the submission in the button's click handler: button.addEventListener('click', (e) => e.preventDefault()). That does not guarantee the form will not submit, because pressing Enter inside a field also fires submit.
  • Forgetting event.preventDefault() and then wondering why the page flashes and the application state resets.
  • Attaching the handler before the form exists in the DOM: document.querySelector('#form') returns null and addEventListener throws.
  • Using return false in a listener added through addEventListener. It does nothing there, unlike in the old inline handlers.
  • Calling preventDefault() after an await: by then the browser has already acted on the event, so the cancellation must be the first thing the handler does.
  • Reading every field by hand with document.querySelector instead of FormData, and losing checkboxes and repeated field names in the process.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.