completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Default actions in JavaScript events

Learn when the browser performs built-in event behavior, when preventDefault can cancel it, how passive listeners affect scrolling, and how defaultPrevented coordinates handlers.

You will learn to
  • 01
    Cancel a default action safelyCall preventDefault() on events that can be canceled, and know that it does not stop the event's trip to parent elements.
  • 02
    Keep scrolling fastExplain what a passive listener is, why wheel and touchmove listeners need it, and when CSS does the job better.
  • 03
    Let listeners work togetherRead defaultPrevented in a later listener, so it can skip a click that another listener already handled.

What the browser does on its own

People click, type, scroll, and send forms on a web page. For many of these, the browser already knows what to do. This built-in behavior is called the default action.

Some default actions you already know:

  • Click a link: the browser goes to the link's page.
  • Send a form: the browser sends what you typed and loads a new page.
  • Click a checkbox: it turns on or off.
  • Press a letter key in a text box: the letter appears.
  • Press Space: the page scrolls down.
  • Right-click: the browser opens its own menu, called the context menu.

Default actions are why a plain HTML page works before you write any JavaScript.

Your JavaScript can listen for the same events. A listener is a function you add with addEventListener. The browser calls it when the event happens. So one click can start two kinds of work: your listeners, and the browser's default action.

The short version

First the browser runs your listeners. Then it does the default action. A listener can stop the default action by calling event.preventDefault(), but only if the event allows it.

Three steps in order. 1: a link is clicked. 2: your listeners run. 3: the browser does its default action and navigates.click1. link clickedlisteners2. your code runsnavigate3. browser default
Your listeners get their turn first. Only after they all finish does the browser do its default action, such as going to the link's page.
Real-life analogyDefault actions are like a plane's autopilot

A plane can fly itself on the usual route. This is called autopilot. Most flights use it, because it is safe and fast. One day there is a storm, so the pilot takes control for that one flight. The pilot does not switch autopilot off forever. The next flight uses it again.

In real life: Autopilot flies the usual route
In JavaScript: The browser goes to the link, sends the form, ticks the box, types, or scrolls
In real life: The pilot takes control for this one flight
In JavaScript: A listener calls event.preventDefault() for this one event
In real life: The next flight uses autopilot again
In JavaScript: The next click has its default action again, unless a listener cancels it again

This lesson uses ideas from three earlier lessons: Introduction to events, Bubbling & capturing, and Event delegation. The main idea you need: one event can run several listeners, one after another, and a later listener can see what an earlier one did.

preventDefault(): stop the default action

STEP THROUGH

Every listener gets an event object. It describes what happened, such as which element was clicked. One of its methods is event.preventDefault(). It tells the browser: "Don't do the default action for this event."

It only works if the event is cancelable. That means the browser allows this event's default action to be stopped. The event tells you: event.cancelable is true or false.

Two things preventDefault() does not do:

  • It does not stop the event's trip. After the clicked element's listeners run, the event moves up to its parent elements, and their listeners still run. This trip is called propagation. To stop it, you call a different method, stopPropagation().
  • It does not undo something that already happened. It only stops the browser's next step.

The step-through below copies a real click with a plain object, so you can watch every line. Lines 1 to 8 make that object. Its preventDefault() sets defaultPrevented to true, but only when cancelable is true. Then come two listeners:

  • linkListener stands for a listener on the link. It runs first.
  • routerListener stands for a listener on a parent element. It runs second. A router is code that changes what the page shows when you click a link.
Step through two listeners and one click
Step 0 of 9Ready
Your turn: follow the blue line

Step through one click that reaches linkListener first and routerListener second. Switch line 11 to see when the router skips.

Running in
  1. script
Next: line 1
  type: "click",  cancelable: true,  defaultPrevented: false,  preventDefault() {    if (this.cancelable) this.defaultPrevented = true;  },}; function linkListener(event) {  event.preventDefault();  console.log("link listener finished");} function routerListener(event) {  if (event.defaultPrevented) {    console.log("router skipped");    return;  }  console.log("router handles the click");} linkListener(event);routerListener(event);console.log(event.defaultPrevented);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Line 11 in linkListener
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

With line 11 calling preventDefault(), linkListener cancels the default action. But routerListener still runs, because nothing stopped the event's trip. On line 16 it reads event.defaultPrevented, sees true, and skips its own work. Switch line 11 to the other choice and run it again: now the router handles the click.

Left: linkListener calls preventDefault(). The event still moves up to routerListener, which still runs. Right: the browser default, navigate, is canceled.routerListenerstill runsthe event moves onlinkListenerpreventDefault()browser defaultnavigatecanceled
preventDefault() cancels only the browser's default action. The event still moves on, so routerListener still runs.

Try real default actions

INTERACTIVE

The best way to learn default actions is to try real ones. The playground below has five real parts of a page:

  • a link
  • a form
  • a checkbox
  • a text box
  • an area you can right-click

Each part has a listener, added with addEventListener. Each listener has a switch. On "prevent", the listener calls preventDefault(). On "allow", it does not, so the default action runs.

Default action switchboard
How each switch decidesPop out in the code editor (opens in a new tab)JavaScript
link.addEventListener("click", maybePreventLink);form.addEventListener("submit", maybePreventForm);checkbox.addEventListener("click", maybePreventCheckbox);function maybePrevent(event, switchIsOn) {  if (switchIsOn && event.cancelable) event.preventDefault();  log(event.type, event.defaultPrevented);}

Try real default actions

Link to a spot on this page

Hash (end of the address): not changed yet

Right-click or long-press this area.
Log
  • Ready. Set a switch, then try a default action.
Try it yourself

Each part of the page here is real and has a real default action. After the listener runs, the log shows the event's cancelable and defaultPrevented values.

Try each part with its switch on prevent, then on allow. The form is sent to a hidden frame, so you never leave the lesson.

The code in the playground shows the idea for three of the parts. Lines 1 to 3 add a listener to the link, the form, and the checkbox. Line 5 calls preventDefault() only when the switch is on and the event is cancelable. Line 6 logs the event type and defaultPrevented.

Try these:

  • Set the link to "allow" and click it. The page jumps, and the hash (the part of the address after #) changes.
  • Set the checkbox to "prevent" and click it. It does not change.
  • Set the input keys to "prevent" and type. No letters appear.
  • Set the context menu to "allow" and right-click the area. The browser may open its own menu.
If you stop a default action, give something back

When you cancel a default action, give people something in its place:

  • Stop a form from being sent? Check it and show a message, or save the data with JavaScript.
  • Stop the right-click menu? Show your own menu, and give keyboard users a way to open it.
  • Stop text selection? Make sure people never need to copy that text.

Passive listeners and scrolling

INTERACTIVE

Scrolling must feel instant. Two events can cancel scrolling:

  • wheel: you turn a mouse wheel or swipe on a trackpad.
  • touchmove: your finger moves on a touch screen.

If one of these events has a listener, the listener might call preventDefault(). So the browser may have to wait for the listener to finish before it moves the page. That wait can make scrolling feel slow.

A passive listener fixes this. You add it with the option { passive: true }. It is a promise to the browser: "I will not call preventDefault()." So the browser can scroll at once, without waiting.

Left: with passive false, the browser waits for the listener before it scrolls, and the listener can cancel the scroll. Right: with passive true, the browser scrolls without waiting, and the listener cannot cancel.{ passive: false }listenerwaitsscrollcan cancel{ passive: true }listenerno waitscrollcannot cancel
A normal listener might cancel the scroll, so the browser waits for it. A passive listener promised not to cancel, so the browser scrolls at once.
Real-life analogyA passive listener is a passenger who promised not to steer

A passenger in a car can still talk, point, and look around. They only promised not to steer. So the driver keeps driving without looking at them.

In real life: The passenger says, 'I will not touch the steering wheel'
In JavaScript: The listener is added with { passive: true }
In real life: The driver keeps going without checking on them
In JavaScript: The browser can start scrolling at once
In real life: The passenger reaches for the wheel anyway; the driver ignores it
In JavaScript: Calling preventDefault() in a passive listener is ignored

A passive listener is the same. It can log and update things, but it cannot stop the scroll. In the playground below, line 7 sets the passive option, and line 4 tries to cancel. Turn each button on and off and scroll inside the box.

Passive vs non-passive scrolling
The passive option is on line 7Pop out in the code editor (opens in a new tab)JavaScript
box.addEventListener("wheel", (event) => {  log("cancelable", event.cancelable);  if (shouldBlock) {    event.preventDefault();  }  log("defaultPrevented", event.defaultPrevented);}, { passive });

A box to scroll

Row 1: scroll me. The browser wants this to move smoothly.

Row 2: scroll me. The browser wants this to move smoothly.

Row 3: scroll me. The browser wants this to move smoothly.

Row 4: scroll me. The browser wants this to move smoothly.

Row 5: scroll me. The browser wants this to move smoothly.

Row 6: scroll me. The browser wants this to move smoothly.

Row 7: scroll me. The browser wants this to move smoothly.

Row 8: scroll me. The browser wants this to move smoothly.

Row 9: scroll me. The browser wants this to move smoothly.

Row 10: scroll me. The browser wants this to move smoothly.

Row 11: scroll me. The browser wants this to move smoothly.

Row 12: scroll me. The browser wants this to move smoothly.

Row 13: scroll me. The browser wants this to move smoothly.

Row 14: scroll me. The browser wants this to move smoothly.

Log
  • Scroll inside the box with a mouse wheel or trackpad.
Try it yourself

Passive is off. The listener can cancel a cancelable wheel or touchmove event, so the box can stop scrolling.

Stop scrolling only when you must. CSS touch-action and overscroll-behavior often do the job better, because the browser knows your plan before any event fires.

What to know about passive listeners:

  • If a passive listener calls preventDefault(), modern browsers ignore the call. defaultPrevented stays false.
  • Some browsers make touchstart, touchmove, and wheel listeners on window, document, or body passive on their own, even if you don't ask.
  • Use { passive: false } only when stopping the scroll is the whole point, like in a drawing area.
  • CSS often does the job better. touch-action tells the browser which finger gestures an element handles itself. overscroll-behavior stops a scroll from moving on to the page behind when an inner box reaches its end.

defaultPrevented: was it canceled?

INTERACTIVE

When preventDefault() works, the event remembers it: event.defaultPrevented becomes true. It is a true-or-false value, called a boolean.

Every later listener gets the same event object. So a later listener can read defaultPrevented and skip its own work.

linkListener calls preventDefault() and sets defaultPrevented to true on the event. Then routerListener reads defaultPrevented on the same event and skips its work.preventDefault()linkListenerskips its workrouterListener1. sets it2. reads itdefaultPrevented: trueon the same event
Both listeners get the same event object. The first one sets defaultPrevented to true. The second one reads it and skips its own work.
Real-life analogydefaultPrevented is a 'done' sticky note

A team shares task cards. When someone finishes a task, they stick a "done" note on its card. Anyone who picks up the card later sees the note and moves on. They don't need to ask who did the task.

In real life: A teammate sticks a 'done' note on a task card
In JavaScript: An earlier listener calls preventDefault()
In real life: Another teammate reads the note and doesn't do the task again
In JavaScript: A later listener checks event.defaultPrevented
In real life: The note is on that one card only
In JavaScript: The value belongs to this one event only

The playground below has two listeners on one parent element. A button stands in for the link. The code on the left works like this:

  • Lines 1 to 3: the first listener cancels the click if it was on a link. event.target is the element that was clicked, and .matches("a") checks that it is an a (link) element.
  • Lines 4 to 7: the second listener returns early if defaultPrevented is true. Otherwise it runs the router.
  • Lines 8 and 9: make a scroll event that is not cancelable, and call preventDefault() on it. Nothing changes.
defaultPrevented with two listeners
Two listeners on one parentPop out in the code editor (opens in a new tab)JavaScript
panel.addEventListener("click", (event) => {  if (event.target.matches("a")) event.preventDefault();});panel.addEventListener("click", (event) => {  if (event.defaultPrevented) return;  runRouterBehavior();});const scroll = new Event("scroll", { cancelable: false });scroll.preventDefault();

Click a button

Log
  • Click the link-style button. Then try the scroll event button.
Try it yourself

The router doesn't need to know which listener ran first. It only reads event.defaultPrevented on the shared event object.

The scroll button makes a real scroll event with cancelable: false. Calling preventDefault() on it changes nothing: defaultPrevented stays false.

You will see this a lot with event delegation, where one listener on a parent element handles clicks for many children. The parent's listener checks defaultPrevented first. If a child already canceled the default action, the parent leaves the click alone.

Can this event be canceled?

SORT IT

Not every event can stop its default action. The scroll event is the common trap. It fires after the page has already scrolled, so there is nothing left to stop. Its cancelable is false.

To stop scrolling, cancel the event that comes before the scroll: the wheel event, the touchmove event, or the key press that would scroll the page.

Can you prevent it?
  • Going to a link's page, from a click
  • Sending a form, from a submit event
  • A checkbox turning on or off, from a click
  • Text selection that starts on mousedown
  • The right-click menu, from contextmenu
  • The scroll event itself
  • The page closing, once it is already closing
  • A letter typed into a text box, from keydown
Try it yourself
0 of 8 correct

Sort each item: is this event the right place to call preventDefault()?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

The table below has one row for each thing you might want to stop. Read across to see which event to cancel, and which event is the wrong one.

Which event to cancel for each default action
What you want to stopCancel this eventNot this one
Going to a link's pageThe link's click event.An event that fires after the new page starts loading. It is too late.
A form being sent and a new page loadingThe form's submit event.A click on one submit button. Pressing Enter can send the form too.
A typed letter appearingThe keydown event for that key, or another event that fires before the text changes.Reading the text box's value afterward. The letter is already there.
The page scrollingThe wheel, touchmove, or key event that causes the scroll.The scroll event. It fires after the page has scrolled.
The right-click menuThe contextmenu event, in most browsers.A click that comes after the menu is already open.

Where you will use this

Cancel a default action when you replace it with your own behavior. Your behavior must work for everyone, including people who use only a keyboard. Here are the patterns you will see most.

Save a form without loading a new pagePop out in the code editor (opens in a new tab)JavaScript
form.addEventListener("submit", (event) => {  event.preventDefault();  saveWithJavaScript(new FormData(form));});

Line 2 stops the form from being sent and a new page loading. Line 3 saves the data with JavaScript instead. new FormData(form) collects every field in the form. Show people what is happening: a loading message, then a success or error message.

Check a form before it is sentPop out in the code editor (opens in a new tab)JavaScript
validationForm.addEventListener("submit", (event) => {  const message = validationForm.elements.message.value.trim();  if (!message) {    event.preventDefault();    validationError.textContent = "Enter a message";    return;  }  console.log("ready to submit", message);});
  • Line 2 reads the message box. .trim() removes spaces at the start and end, so a message of only spaces counts as empty.
  • If the message is empty, line 4 stops the form from being sent, and line 5 shows an error message on the page.
  • If there is a message, the listener does not cancel. Line 8 logs it, and the form is sent as normal.

Put this check on the form's submit event, not on the button's click. People can also send a form by pressing Enter, and submit catches every way.

Open a pop-up box from a linkPop out in the code editor (opens in a new tab)JavaScript
modalLink.addEventListener("click", (event) => {  event.preventDefault();  modal.hidden = false;  modalClose.focus();}); modalClose.addEventListener("click", () => {  modal.hidden = true;  modalLink.focus();});

A modal is a box that opens on top of the page. Here a link opens it:

  • Line 2 stops the link from going to its address.
  • Line 3 shows the modal by setting hidden to false.
  • Line 4 moves keyboard focus to the modal's Close button, so keyboard users land inside it.
  • Lines 8 and 9 hide the modal again and move focus back to the link.

The link still has a real address in its href. If JavaScript does not run, clicking it still goes somewhere useful.

Replace the right-click menu, and keep a button for itPop out in the code editor (opens in a new tab)JavaScript
menuArea.addEventListener("contextmenu", (event) => {  event.preventDefault();  openMenu(event.clientX, event.clientY);}); menuButton.addEventListener("click", () => openMenu(0, 0));menuButton.addEventListener("keydown", (event) => {  if (event.key === "Enter" || event.key === " ") openMenu(0, 0);});
  • Line 1 listens for contextmenu, the event for a right-click or a long press on a touch screen.
  • Line 2 stops the browser's own menu, but only inside this area.
  • Line 3 opens your menu where the pointer is. clientX and clientY are the pointer's position.
  • Lines 6 to 8 give another way in: a visible button that opens the same menu with a click, or with Enter or Space.

In a full menu, also close it with Escape and move focus back to where it was.

Stop one letter from being typedPop out in the code editor (opens in a new tab)JavaScript
input.addEventListener("keydown", (event) => {  if (event.key === "x") {    event.preventDefault();  }});

Line 2 checks which key was pressed. Line 3 cancels only the x key, so x never appears. Every other key types as normal.

Be careful with keys. People rely on the arrow keys, Tab, Space, Enter, and browser shortcuts. Cancel only the exact key you replace, and give another way to do the same thing.

  • Single-page apps

    A link goes to another page on the same site. Cancel the click, change the address with the History API (the browser's tool for the address bar), and show the new content. No whole new page loads.

  • Drawing and maps

    In a drawing area or a map, cancel only the finger and mouse moves your app handles itself. Use CSS touch-action when it can do the job.

  • Widgets inside a parent

    A child widget calls preventDefault(). The listener on the parent checks defaultPrevented before it does anything.

Common mistakes

COMMON TRAPS
  • "preventDefault() stops bubbling." No. It only cancels the default action. The event still moves up to the parent elements. Stopping that is a separate choice.
  • "stopPropagation() stops the default action." No. It only stops the event from reaching parent elements. A link still goes to its page.
  • "return false always cancels." Not in a listener added with addEventListener. There, the browser ignores what you return. Call preventDefault() instead.
  • "Passive is just a faster way to write a listener." No. Passive means the listener cannot cancel the default action.
  • "Every event has a default action you can cancel." No. Check event.cancelable, and cancel the right earlier event.
  • "Stopping a default action is harmless." Many default actions help people who use a keyboard or a screen reader. If you remove one, replace it.
return false: ignored in addEventListener, works in onclickPop out in the code editor (opens in a new tab)JavaScript
button.addEventListener("click", (event) => {  return false; // does not cancel an addEventListener default}); button.onclick = function (event) {  return false; // old property handlers cancel the default};
  • Line 2 returns false from a listener added with addEventListener. The browser ignores it, so the default action still happens.
  • Line 6 returns false from an old-style onclick handler, a function stored in the element's onclick property. There, false does cancel the default action.

This difference is old and easy to mix up. Always call event.preventDefault(), so anyone reading your code knows what you mean.

The table below compares four tools. Read across each row to see what the tool changes and what it leaves alone.

Four event tools people mix up
ToolWhat it changesWhat it leaves alone
preventDefault()It cancels the default action, if the event is cancelable.The event still moves up to the parent elements.
stopPropagation()The parent elements' listeners don't get the event.The default action still happens.
{ passive: true }The listener can no longer cancel the default action.The listener can still read the event and run code.
defaultPreventedNothing. It only tells you whether the default action was canceled.It cannot make an event cancelable.

Practice

5 EXERCISES
Exercise 1 · Warm-upPredict the two listeners

Write the three lines the console prints, in order. Separate them with commas.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const event = { defaultPrevented: false, cancelable: true };
event.preventDefault = function () {
  if (this.cancelable) this.defaultPrevented = true;
};
function linkListener(event) {
  event.preventDefault();
  console.log("link listener finished");
}
function routerListener(event) {
  if (event.defaultPrevented) {
    console.log("router skipped");
    return;
  }
  console.log("router handles the click");
}
linkListener(event);
routerListener(event);
console.log(event.defaultPrevented);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upCancelable or not?

    Predict the one line the console prints.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const scroll = new Event("scroll", { cancelable: false });
    scroll.preventDefault();
    console.log(scroll.defaultPrevented);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeStop one typed letter

      Which method goes inside the if block to stop the x key?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      input.addEventListener("keydown", (event) => {
        if (event.key === "x") {
          event.preventDefault();
        }
      });

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFind the bug: the link still goes to its page

        The developer wanted the link to stay on this page. But it still goes to its page. Explain the bug, then compare your fix with the solution.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        link.addEventListener("click", (event) => {
          saveDraft();
          return false;
        });
          Exercise 5 · ChallengePlan a custom menu that everyone can use

          Plan the events for your own right-click menu in a drawing app. Say which event you cancel. Then give one other way to open the menu, for example for keyboard users.

            Check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7When does the browser usually do the default action for an event?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this code print? The scroll event is not cancelable.

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const event = new Event("scroll", { cancelable: false });
              event.preventDefault();
              console.log(event.defaultPrevented);

              Choose an answer to see the explanation.

            3. Question 3 of 7Which sentence about preventDefault() is true?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does this code print? The click event is cancelable.

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const event = new Event("click", { cancelable: true });
              event.preventDefault();
              console.log(event.defaultPrevented);

              Choose an answer to see the explanation.

            5. Question 5 of 7A passive wheel listener calls event.preventDefault(). What happens?

              Choose an answer to see the explanation.

            6. Question 6 of 7What does return false do inside a listener added with addEventListener?

              Choose an answer to see the explanation.

            7. Question 7 of 7Why would a router listener on a parent element check event.defaultPrevented?

              Choose an answer to see the explanation.

            Key takeaways

            • A default action is what the browser does on its own after an event.
            • preventDefault() works only on cancelable events. A passive listener cannot cancel at all.
            • The browser does the default action after every listener has run. So a later listener can still read defaultPrevented.
            • Canceling the default action and stopping the event's trip to parent elements are two separate choices.
            • Many default actions help people. Don't remove keyboard, scrolling, selection, form, or menu behavior without giving something in its place.

            A default action is what the browser does on its own after an event. event.preventDefault() cancels it, but only when the event is cancelable and the listener is not passive.

            Up next: Custom events.

            CompleteFrontend Clear concepts. Working examples.