Turning a handler off
.off() is the opposite of .on(). To remove a specific handler, pass the same event name and the same function you attached:
<button id="hit">Hit</button>
<button id="stop">Stop counting</button>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
let hits = 0;
function count() {
hits++;
console.log("hits:", hits);
}
$("#hit").on("click", count);
$("#stop").on("click", function () {
$("#hit").off("click", count);
console.log("counting stopped");
});
</script>hits: 1
hits: 2
counting stoppedThe third click on Hit, after Stop, did nothing. To remove a handler you need a reference to it, which is one more reason to use named functions for handlers you may turn off. An anonymous function written inline cannot be passed to .off(), because writing the same code again creates a different function.
Removing all handlers of a type
.off("click") with no function removes every click handler on the element that was attached with jQuery. .off() with no arguments removes all handlers of all types. This is blunt: it also removes handlers that other code, such as a plugin or a theme, attached to the same element.
For delegated handlers, pass the same selector: $("#list").off("click", "li").
Running once with .one()
.one() attaches a handler that removes itself after it runs:
<button id="welcome">Show welcome tip</button>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#welcome").one("click", function () {
console.log("Tip: press / to search");
$(this).text("Tip shown");
});
$("#welcome").on("click", () => console.log("button clicked"));
</script>Tip: press / to search
button clicked
button clicked
button clickedThe tip ran on the first click only; the ordinary handler ran every time. If .one() is attached to several elements, it runs once per element.
A practical use is stopping double submissions: attach the submit handler with .one() (or disable the button on the first submit) so an impatient double-click does not send an order twice.
The stacking bug
A very common bug in jQuery code: a handler is attached inside something that runs repeatedly, so copies pile up.
<button id="open">Open dialog</button>
<button id="save">Save</button>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
let saves = 0;
$("#open").on("click", function () {
// Bug: a new copy of the handler is added every time the dialog opens
$("#save").on("click", () => saves++);
});
$("#save").on("click", () => setTimeout(() => console.log("save ran", saves, "time(s) for one click")));
</script>save ran 3 time(s) for one clickThe dialog was opened three times, so one click on Save ran the handler three times. In a real app that could mean three identical orders. Fixes, from best to quickest:
- attach the handler once, outside the code that repeats;
- use delegation, attached once;
- remove before adding:
$("#save").off("click.dialog").on("click.dialog", save), which uses a namespace, explained next.
Namespaces
A namespace is a label after a dot in the event name: click.menu, keydown.search. It does not change when the handler runs; it lets you refer to a group of handlers later:
<button id="btn">Button</button>
<button id="remove">Remove my handlers</button>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
// Another script's handler
$("#btn").on("click", () => console.log("analytics: click recorded"));
// Our handlers, labelled with the namespace .tip
$("#btn").on("click.tip", () => console.log("tip: shown"));
$("#btn").on("mouseenter.tip", () => console.log("tip: hover"));
$("#remove").on("click", function () {
$("#btn").off(".tip");
console.log("removed .tip handlers");
});
</script>analytics: click recorded
tip: shown
removed .tip handlers
analytics: click recorded.off(".tip") removed both of our handlers (click and mouseenter) and left the analytics handler alone. Plugin authors use namespaces so that their cleanup code never removes the site's own handlers. Use them whenever your code shares elements with other code.
You can also trigger only namespaced handlers: .trigger("click.tip") runs the .tip click handlers and not the others.
Common mistakes
- Trying to remove an anonymous function with
.off(). - Calling
.off("click")and removing other scripts' handlers. - Attaching handlers inside repeated code, so they stack.
- Forgetting the selector when removing a delegated handler.
Exercise
Make a Subscribe button that, when clicked, logs subscribed and then cannot log again however often it is clicked. Do it in two ways: with .one(), and with .on() plus .off() using a namespace.
Show one possible answer
<button id="s1">Subscribe (one)</button>
<button id="s2">Subscribe (off)</button>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#s1").one("click", () => console.log("subscribed"));
$("#s2").on("click.subscribe", function () {
console.log("subscribed");
$(this).off(".subscribe");
});
</script>subscribed
subscribedKey points
- .off("click", handler) removes that handler; .off("click") removes every click handler on the element.
- .one("click", handler) runs the handler at most once per element, then removes it.
- Namespaces like "click.menu" label handlers, so .off(".menu") removes only yours.
- Attaching a handler inside code that runs repeatedly stacks copies; remove first or attach once.