Using Preventdefault in JavaScript: Full Guide
Learn how preventDefault works, when to use it, and when to avoid it. Covers links, forms, checkboxes, keyboard events, and the difference from stopPropagation.
event.preventDefault() tells the browser: "Do not perform the default action for this event." Every event type has a default behavior that the browser performs automatically, such as a link navigating to a URL, a keypress inserting a character, or a form submission sending data to a server.
preventDefault() blocks that built-in behavior so you can replace it with your own.
Here is the simplest example, stopping a link from navigating:
const link = document.querySelector("a");
link.addEventListener("click", (event) => {
event.preventDefault();
console.log("Navigation prevented. You are still on the same page.");
});Click the link and the page does not go anywhere. The browser's default navigation is blocked, but your console message still runs.
What Default Actions Can You Prevent?
| Event | Default Action | When to Prevent It |
|---|---|---|
click on <a> | Navigate to the href URL | Custom client-side routing, confirmation dialogs |
submit on <form> | Send data and reload the page | Custom validation, AJAX submission |
click on <input type="checkbox"> | Toggle the checked state | Conditional toggling |
contextmenu | Show the browser's right-click menu | Custom context menus |
keydown on a text input | Insert the typed character | Restricting allowed characters, keyboard shortcuts |
wheel / touchmove | Scroll the page | Custom scroll behavior |
dragstart on an image | Start a drag operation | Disabling image dragging |
Not all events have cancelable default actions. The scroll event on an element, for example, cannot be canceled because the scrolling already happened by the time the event fires.
Checking if an Event Is Cancelable
Before calling preventDefault(), check event.cancelable. If it is false, the call has no effect:
document.addEventListener("scroll", (event) => {
console.log(event.cancelable); // false on elements, true on document
event.preventDefault(); // Has no effect on elements
});Most user-initiated events are cancelable. Events dispatched programmatically with dispatchEvent are not cancelable unless you explicitly pass { cancelable: true }.
Practical Use Cases
Intercepting Link Clicks for Client-Side Navigation
In single-page applications, link clicks should not cause a full page reload:
document.addEventListener("click", (event) => {
const link = event.target.closest("a");
if (!link) return;
event.preventDefault();
const url = link.getAttribute("href");
console.log(`Navigate to ${url} without reloading the page`);
// Update the page content with JavaScript instead
});The listener sits on document and uses closest("a") to catch clicks on any link, even ones nested inside other elements. Blocking the default navigation lets a router swap page content without a full reload.
Validating a Form Before Submission
By default, a form submits and reloads the page as soon as the user clicks submit. Calling preventDefault() inside the submit handler lets you check the data first and only allow the submission to continue when it passes:
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
const emailInput = form.querySelector('input[type="email"]');
if (!emailInput.value.includes("@")) {
event.preventDefault();
console.log("Invalid email. Form not submitted.");
}
});The form only submits when the email contains an @. This is a basic example; real validation should also use HTML constraint attributes like required and type="email".
Blocking the Context Menu for a Custom One
document.addEventListener("contextmenu", (event) => {
event.preventDefault();
console.log("Right-click at:", event.clientX, event.clientY);
// Show your own custom menu here
});Restricting Keyboard Input
You can also use preventDefault() with keydown to stop specific characters from ever reaching an input field, which is useful for fields that should only accept digits:
const input = document.querySelector("input");
input.addEventListener("keydown", (event) => {
// Allow only digits
if (!/^\d$/.test(event.key) && event.key !== "Backspace") {
event.preventDefault();
console.log(`Blocked character: ${event.key}`);
}
});Note: keydown fires before the character appears in the input, so preventDefault() blocks it entirely. This is useful for numeric-only fields, but for most input restrictions, prefer the input event with post-validation or HTML attributes like pattern.
preventDefault vs stopPropagation
These two methods are often confused, but they do completely different things:
| Method | What it does |
|---|---|
event.preventDefault() | Blocks the browser's default action (navigation, form submit, etc.) |
event.stopPropagation() | Stops the event from bubbling up to parent elements |
They are independent. Calling one does not affect the other. You can call both, just one, or neither depending on what you need:
link.addEventListener("click", (event) => {
event.preventDefault(); // Do not navigate
event.stopPropagation(); // Do not let parent elements know about this click
console.log("Click intercepted completely.");
});The passive Listener Trap
When you set { passive: true } on a listener, you are promising the browser that you will not call preventDefault(). If you break that promise, the browser ignores the call and logs a warning:
// This will not work: preventDefault is ignored inside a passive listener
document.addEventListener("wheel", (event) => {
event.preventDefault(); // Ignored! Browser scrolls anyway.
}, { passive: true });This is intentional. The passive option lets the browser start scrolling immediately without waiting for your JavaScript to finish, which improves scroll responsiveness. If you genuinely need to prevent the default scroll behavior, you must use { passive: false }:
document.addEventListener("wheel", (event) => {
event.preventDefault(); // Works because passive is false
}, { passive: false });In browsers other than Safari, wheel, touchstart, and touchmove already default to passive: true on Window, Document, and Document.body, so always set { passive: false } explicitly when you need preventDefault on those events.
Better Alternatives to preventDefault
Before reaching for preventDefault(), consider whether a simpler solution exists:
| Instead of preventing... | Use |
|---|---|
| Click on a form control | disabled or readonly HTML attribute |
| Link navigation | href="javascript:void(0)" or a <button> styled as a link |
| Form submission with invalid data | HTML constraint validation (required, pattern, minlength) |
| Scrolling | CSS overflow: hidden |
| Text input | maxlength attribute or pattern regex |
| Right-click menu | CSS oncontextmenu="return false" (presentational only) |
preventDefault is called inside event listeners like click and keydown. If you need a refresher on setting up those listeners, see /javascript/how-to-add-event-listeners-in-js-complete-guide and /javascript/javascript-keyboard-events-keyup-and-keydown.
preventDefault is a JavaScript solution. When an HTML or CSS solution achieves the same result, it is usually simpler, more accessible, and works even before JavaScript loads. Use preventDefault when you need conditional logic, like "only prevent the default if the user has unsaved changes," that cannot be expressed declaratively.
Common Mistakes
Calling preventDefault on a non-cancelable event. Check event.cancelable first if you are unsure.
Forgetting to set { passive: false } on scroll handlers. If your wheel or touchmove handler needs preventDefault, pass the option explicitly.
Assuming preventDefault stops propagation. It does not. The event still bubbles. Use stopPropagation() if you need that behavior.
Using preventDefault when HTML attributes would suffice. A disabled attribute on a button is more robust and accessible than blocking clicks with JavaScript.
Rune AI
Key Insights
- event.preventDefault() blocks the browser's built-in default action for an event.
- Use it for intercepting link clicks, validating forms, and preventing context menus.
- It does not stop event propagation; use stopPropagation for that.
- Check event.cancelable before calling preventDefault.
- Passive listeners cannot call preventDefault; use { passive: false } if needed.
- Prefer HTML attributes like disabled or readonly when they solve the same problem.
Frequently Asked Questions
Does preventDefault stop event bubbling?
Can I undo preventDefault?
Why does preventDefault fail inside a passive listener?
Conclusion
preventDefault stops the browser from performing its default action in response to an event. Use it when you need to intercept a link click for custom navigation, validate a form before submission, or block a context menu. Always check event.cancelable first, and prefer HTML and CSS alternatives when they solve the same problem more cleanly.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.