Firing an event from code
.trigger(eventName) makes an event happen as if the user did it. Every handler attached to the element runs, and the event bubbles up through its ancestors:
<div id="wrap">
<button id="save">Save</button>
</div>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#save").on("click", () => console.log("save handler ran"));
$("#wrap").on("click", () => console.log("wrapper heard the click"));
$("#save").trigger("click");
</script>save handler ran
wrapper heard the clickCommon uses:
- running the same code a button runs, for example
$("#refresh").trigger("click")after loading the page; - making handlers react after code changes a value:
$("#country").val("IN").trigger("change"), as in the form values lesson; - moving focus:
$("#name").trigger("focus").
As you saw in the events lesson, the shorthand .click() with no arguments does the same as .trigger("click") but is deprecated; write .trigger().
Default actions
For native events that have a default action, .trigger() also runs that action. Triggering focus really focuses the field; triggering submit on a form runs its submit handlers and then submits it, unless a handler calls preventDefault(). Triggering click on a checkbox ticks it:
<label><input id="news" type="checkbox"> Newsletter</label>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#news").on("click", function () {
console.log("handler sees checked =", this.checked);
});
$("#news").trigger("click");
console.log("after trigger, checked =", $("#news").prop("checked"));
</script>handler sees checked = true
after trigger, checked = trueOnly the handlers: .triggerHandler()
Sometimes you want the handler's logic without side effects. .triggerHandler():
- runs handlers on the first element only;
- does not bubble;
- does not run the default action;
- returns whatever the last handler returned.
<form id="f"><input id="age" value="15"></form>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#age").on("validate", function () {
return Number($(this).val()) >= 18;
});
$("#f").on("validate", () => console.log("form heard validate"));
const ok = $("#age").triggerHandler("validate");
console.log("is valid?", ok);
</script>is valid? falseThe form's handler did not run, because .triggerHandler() does not bubble, and the return value came straight back.
Passing data
Pass an array as the second argument to .trigger(); its values arrive as extra arguments after the event object:
<p id="status"></p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#status").on("saved", function (event, id, state) {
$(this).text("Saved item " + id + " as " + state);
console.log(event.type, id, state);
});
$("#status").trigger("saved", [42, "draft"]);
</script>saved 42 draftCustom events
The event name in that example, saved, is not a browser event. jQuery lets you trigger and listen for any name you choose. That turns events into a simple messaging system between parts of a page.
Imagine a shop page with a cart, a badge in the header and a running total. Without custom events, the cart code must update the badge and the total itself, and every new feature means editing the cart. With a custom event, the cart just announces what happened:
<header>Cart <span id="badge">0</span> · Total <span id="total">0</span></header>
<div id="products">
<button data-name="Lamp" data-price="1200">Add lamp</button>
<button data-name="Pen" data-price="40">Add pen</button>
</div>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
// The cart: keeps the data and announces changes
const cart = [];
$("#products").on("click", "button", function () {
cart.push($(this).data());
$(document).trigger("cart:updated", [cart]);
});
// Independent listeners: each one only knows about the event
$(document).on("cart:updated", function (event, items) {
$("#badge").text(items.length);
});
$(document).on("cart:updated", function (event, items) {
const sum = items.reduce((total, item) => total + item.price, 0);
$("#total").text(sum);
});
$(document).on("cart:updated", function (event, items) {
console.log("analytics: cart has", items.length, "item(s), last added", items.at(-1).name);
});
</script>analytics: cart has 1 item(s), last added Lamp
analytics: cart has 2 item(s), last added Pen
analytics: cart has 3 item(s), last added LampThe cart code never mentions the badge, the total or the analytics. To add a "free delivery over 1,000" banner later, you add one more listener and change nothing else. Code that is connected only through events like this is called loosely coupled, and it is much easier to change safely.
Naming tips:
- use a prefix with a colon or dot, such as
cart:updatedormodal:open, so your names never clash with real browser events; - name events in the past tense for things that happened (
updated,saved), and trigger them on the element they are about, or ondocumentfor page-wide news.
Custom events in plain JavaScript
Browsers support custom events natively with new CustomEvent("cart:updated", { detail: cart }) and dispatchEvent(). jQuery's version is shorter and passes data as arguments, but the idea is the same, and jQuery handlers do not hear native custom events dispatched this way unless you listen with jQuery's .on() and read event.originalEvent.detail.
Common mistakes
- Using the deprecated
.click()shorthand to trigger. - Expecting
.triggerHandler()to bubble or to run on every element. - Triggering
submitwithout realising it really submits the form when no handler prevents it. - Custom event names without a prefix that later clash with a real event.
Exercise
Create a custom event called theme:change triggered on document with the theme name as data. One listener sets the body's background (light or dark), and another logs the new theme. Add two buttons that trigger it with light and dark.
Show one possible answer
<button id="light">Light</button> <button id="dark">Dark</button>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$(document).on("theme:change", (event, theme) => {
$("body").css({ background: theme === "dark" ? "#0f172a" : "#ffffff", color: theme === "dark" ? "#f8fafc" : "#0f172a" });
});
$(document).on("theme:change", (event, theme) => console.log("theme is now", theme));
$("#light, #dark").on("click", function () {
$(document).trigger("theme:change", [this.id]);
});
</script>theme is now dark
theme is now lightKey points
- .trigger("click") runs the element's handlers as if the event happened, and bubbles like a real event.
- For click, submit and focus, .trigger() also runs the browser's default action; .triggerHandler() runs only the handlers.
- Extra values passed to .trigger("name", [a, b]) arrive as extra handler arguments.
- Custom events such as "cart:updated" let one part of the page announce something and others react, without knowing about each other.