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
submitcollects the fields, sends an HTTP request toactionand reloads the page. event.preventDefault()inside thesubmithandler cancels that default behaviour.- The form values are easiest to read with
new FormData(form)andObject.fromEntries(formData.entries()). - The request is then sent manually:
fetch()or any HTTP client. - Listen to
submiton the form, not toclickon the button: a form is also submitted by Enter inside a field. - The inline
onsubmitattribute works too, but it mixes markup with logic and is not recommended.
Quick example
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
<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:
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
<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
<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 do | The right way |
|---|---|
| Cancel the page reload | event.preventDefault() in the submit handler |
| Read the form data | new FormData(form) |
| Send the data manually | fetch() or the HTTP client of your choice |
| The reliable approach | Listen to submit, not to click |
Just remember: so that the form does not reload the page, always use
javascriptform.addEventListener('submit', (e) => e.preventDefault());
Common mistakes
- Cancelling the submission in the button's
clickhandler:button.addEventListener('click', (e) => e.preventDefault()). That does not guarantee the form will not submit, because pressing Enter inside a field also firessubmit. - 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')returnsnullandaddEventListenerthrows. - Using
return falsein a listener added throughaddEventListener. It does nothing there, unlike in the old inline handlers. - Calling
preventDefault()after anawait: 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.querySelectorinstead ofFormData, and losing checkboxes and repeated field names in the process.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.