Events with on() and off()

jQuery · Lesson 12 of 24 6 min read Updated

An event is something that happens on the page: a click, a key press, the mouse moving over a card, a form being submitted. Your code reacts by attaching a handler (a function the browser calls when the event happens). Events are where most interactive behaviour starts.

In this lesson you will attach and remove handlers with jQuery's .on(), .off() and .one(), use the event object, and learn the plain JavaScript versions with addEventListener(). You will also learn which old jQuery event methods to replace when you meet them.

Attaching a handler with on()

.on(eventName, handler) attaches the handler to every element in the set. Inside the handler, this is the DOM element that the handler is attached to, and the first parameter is the event object.

JavaScript
$("#like").on("click", function (event) {
  $(this).toggleClass("liked");
  console.log(event.type);           // "click"
});

// Plain JavaScript
document.querySelector("#like").addEventListener("click", function (event) {
  this.classList.toggle("liked");
  console.log(event.type);
});

You can attach one handler to several events by separating names with spaces, or give an object that maps event names to handlers:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>on() basics</title>
  <style>
    .card { border: 1px solid #ccc; border-radius: 8px; padding: 0.7rem; max-inline-size: 20rem; transition: box-shadow 0.2s; }
    .card.hover { box-shadow: 0 4px 14px rgb(0 0 0 / 0.18); }
    .liked { background: #ffe3ea; }
  </style>
</head>
<body>
  <div class="card" id="card">
    <h3>Rajma chawal recipe</h3>
    <p>By Ananya, Lucknow</p>
    <button id="like">Like</button>
    <span id="likes">0 likes</span>
  </div>
  <p>
    <label>Add a comment and press Enter: <input id="comment"></label>
  </p>
  <ul id="comments"></ul>

  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    let likes = 0;

    // An object of event names and handlers
    $("#card").on({
      mouseenter: function () { $(this).addClass("hover"); },
      mouseleave: function () { $(this).removeClass("hover"); },
    });

    $("#like").on("click", function () {
      const liked = $(this).toggleClass("liked").hasClass("liked");
      likes += liked ? 1 : -1;
      $(this).text(liked ? "Unlike" : "Like");
      $("#likes").text(likes + (likes === 1 ? " like" : " likes"));
    });

    // Keyboard: check event.key, not key codes
    $("#comment").on("keydown", function (event) {
      if (event.key === "Enter" && this.value.trim()) {
        $("<li>").text(this.value.trim()).appendTo("#comments");
        this.value = "";
      }
    });
  </script>
</body>
</html>

Note event.key: it gives readable names such as "Enter", "Escape" or "a". Old code checks numeric codes like event.which === 13; jQuery 4 removed its which fix-up, so use event.key.

The event object

jQuery wraps the browser's event in its own object with the same useful properties. The original browser event is still available as event.originalEvent.

Property or methodMeaning
event.targetthe element where the event started (for example, an <img> inside a button)
event.currentTargetthe element the handler is attached to (same as this in a normal function)
event.typethe event name, such as "click"
event.keywhich key was pressed, for keyboard events
event.preventDefault()stop the browser's default action, such as following a link or submitting a form
event.stopPropagation()stop the event bubbling up to parent elements

In a jQuery handler, return false does both preventDefault() and stopPropagation(). In a plain addEventListener handler, return false does nothing at all. This difference causes real bugs when people convert code, so prefer calling event.preventDefault() explicitly in both styles.

JavaScript
$("#signup-form").on("submit", function (event) {
  event.preventDefault();          // clearer than "return false"
  // ...validate and send with AJAX instead
});

Removing handlers: off(), namespaces and one()

.off("click") removes every click handler that jQuery attached to those elements, including ones added by plugins. To remove only your own, give your handlers a namespace: a label after a dot.

JavaScript
$(document).on("keydown.shortcuts", handleShortcut);
$(window).on("resize.layout scroll.layout", updateLayout);

$(document).off(".shortcuts");          // removes only handlers in that namespace
$(window).off("resize.layout");         // one event in one namespace

.one() attaches a handler that runs only the first time, then removes itself.

In plain JavaScript, removeEventListener needs the same function reference you added, which means you cannot use an inline anonymous function. Two modern tools make this easier: the once option, and an AbortController whose signal removes every listener added with it.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Removing handlers</title>
</head>
<body>
  <p>Press any letter key to log it. Vote once only.</p>
  <button id="vote-jq">Vote: Kochi (jQuery one)</button>
  <button id="vote-js">Vote: Pune (plain once)</button>
  <button id="stop">Stop key logging</button>
  <p id="votes">Votes: Kochi 0, Pune 0</p>
  <p id="keys">Keys: </p>

  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    const votes = { Kochi: 0, Pune: 0 };
    const showVotes = () => $("#votes").text("Votes: Kochi " + votes.Kochi + ", Pune " + votes.Pune);

    // jQuery: runs one time, then removes itself
    $("#vote-jq").one("click", function () {
      votes.Kochi++;
      showVotes();
    });

    // Plain JavaScript: the once option does the same
    document.querySelector("#vote-js").addEventListener("click", () => {
      votes.Pune++;
      showVotes();
    }, { once: true });

    // Namespaced jQuery handler and an AbortController-based plain listener
    $(document).on("keydown.logger", function (event) {
      if (event.key.length === 1) $("#keys").append(document.createTextNode(event.key));
    });
    const controller = new AbortController();
    document.addEventListener("keydown", (event) => {
      if (event.key === "Escape") $("#keys").text("Keys: ");
    }, { signal: controller.signal });

    $("#stop").on("click", function () {
      $(document).off(".logger");   // only our namespaced handler
      controller.abort();           // removes every listener using this signal
      $(this).prop("disabled", true).text("Logging stopped");
    });
  </script>
</body>
</html>

Click each vote button several times: each counts only once. Type a few letters, press Escape to clear, then stop logging.

Old event methods to replace

You will see many older ways of attaching events. They still work in jQuery 4, but are deprecated, so replace them with .on() when you edit that code:

Older codeDeprecated sinceWrite instead
$el.click(fn), .keyup(fn), .submit(fn)...3.3$el.on("click", fn)
$el.hover(inFn, outFn)3.3.on("mouseenter", inFn).on("mouseleave", outFn)
$el.bind("click", fn) / .unbind()3.0.on() / .off()
$(parent).delegate("a", "click", fn)3.0$(parent).on("click", "a", fn)
$el.live("click", fn)removed in 1.9delegation with .on()

Calling a shorthand with no arguments, such as $("#save").click(), triggers the event instead. Write .trigger("click") to make that intention obvious; the plain version is element.click().

Common mistakes

  • Using this inside an arrow function handler. Arrow functions do not get their own this. Use event.currentTarget, or a normal function.
  • Using return false in a plain listener. It does nothing; call event.preventDefault().
  • Calling .off("click") without a namespace. It also removes handlers added by plugins and other scripts.
  • Attaching handlers inside another handler. Each click adds one more handler, so the code runs twice, then three times. Attach once, outside.
  • Passing removeEventListener a new anonymous function. It is a different function, so nothing is removed.

Key points

  • .on(events, handler) attaches handlers; addEventListener is the plain equivalent.
  • The event object gives target, currentTarget, type, key, preventDefault() and stopPropagation().
  • return false in jQuery means "prevent default and stop propagation"; in plain listeners it means nothing.
  • Use namespaces (click.menu) with .off(), and .one() for run-once handlers; plain JavaScript uses { once: true } and AbortController.
  • Replace deprecated .click(fn), .hover(), .bind() and .delegate() with .on().

Exercise

Make a "Show password" checkbox next to a password input. With jQuery, use .on("change", ...) to switch the input's type between "password" and "text". Add a second handler, in the namespace .hint, that shows "Caps Lock is on" under the input when event.originalEvent.getModifierState("CapsLock") is true on keyup. Add a button that removes only the hint handler.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Password helpers</title>
</head>
<body>
  <label>Password <input type="password" id="pw"></label>
  <label><input type="checkbox" id="show"> Show password</label>
  <p id="hint"></p>
  <button id="no-hint">Turn off Caps Lock hint</button>
  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    $("#show").on("change", function () {
      $("#pw").attr("type", this.checked ? "text" : "password");
    });
    $("#pw").on("keyup.hint", function (event) {
      const caps = event.originalEvent.getModifierState("CapsLock");
      $("#hint").text(caps ? "Caps Lock is on" : "");
    });
    $("#no-hint").on("click", function () {
      $("#pw").off(".hint");
      $("#hint").text("");
    });
  </script>
</body>
</html>

getModifierState belongs to the browser's own event, so it is reached through event.originalEvent, and .off(".hint") removes only the namespaced handler.

Quick Quiz

1. What does return false do inside a jQuery .on() handler?

2. How do you remove only your own click handlers from #menu in jQuery?

3. What is the plain JavaScript equivalent of .one("click", fn)?

4. Which property should you check to see if the Enter key was pressed?

Learn jQuery with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call