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.

5 min read

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:

javascriptjavascript
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?

EventDefault ActionWhen to Prevent It
click on <a>Navigate to the href URLCustom client-side routing, confirmation dialogs
submit on <form>Send data and reload the pageCustom validation, AJAX submission
click on <input type="checkbox">Toggle the checked stateConditional toggling
contextmenuShow the browser's right-click menuCustom context menus
keydown on a text inputInsert the typed characterRestricting allowed characters, keyboard shortcuts
wheel / touchmoveScroll the pageCustom scroll behavior
dragstart on an imageStart a drag operationDisabling 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:

javascriptjavascript
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

In single-page applications, link clicks should not cause a full page reload:

javascriptjavascript
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:

javascriptjavascript
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

javascriptjavascript
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:

javascriptjavascript
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:

MethodWhat 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:

javascriptjavascript
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:

javascriptjavascript
// 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 }:

javascriptjavascript
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 controldisabled or readonly HTML attribute
Link navigationhref="javascript:void(0)" or a <button> styled as a link
Form submission with invalid dataHTML constraint validation (required, pattern, minlength)
ScrollingCSS overflow: hidden
Text inputmaxlength attribute or pattern regex
Right-click menuCSS 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

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.
RunePowered by Rune AI

Frequently Asked Questions

Does preventDefault stop event bubbling?

No. preventDefault only blocks the browser's built-in default action. The event still bubbles up to parent elements. If you need to stop propagation, use event.stopPropagation() separately.

Can I undo preventDefault?

No. Once preventDefault is called, the default action is blocked for that event. There is no method to undo it. Check event.defaultPrevented to see if it has already been called.

Why does preventDefault fail inside a passive listener?

Passive listeners declare upfront that they will not call preventDefault. If you call it anyway, the browser ignores it and logs a warning. Set { passive: false } explicitly when you know you need preventDefault for scroll or touch events.

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.