Delegation & Event Namespaces
Scale event handling with delegation and keep it tidy with namespaced events.
You already met basic delegation: $(parent).on("click", "child", fn). As an app grows, two advanced habits keep your event layer sane.
Namespaces — label your handlers
Add a .something suffix to an event name and you can later remove just your handlers without disturbing anyone else's:
$(document).on("click.myWidget", "button", handler);
$(window).on("resize.myWidget", onResize);
// Later, tear down everything this widget attached β and nothing else:
$(document).off(".myWidget");
$(window).off(".myWidget");This is how well-behaved widgets clean up after themselves. Without namespaces, $(document).off("click") would nuke every click handler on the page.
Delegation that survives re-rendering
Because the listener lives on a stable ancestor, delegated handlers keep working when you replace the inner HTML wholesale — the classic pain point of “my buttons stopped working after I reloaded the list.”
| Approach | Handles future elements? | Number of listeners |
|---|---|---|
| Direct bind on each child | No | One per child |
| Delegated on parent | Yes | One, total |
Inside a delegated handler, this is the matched child, while event.target is the exact element clicked (which may be a descendant of it).
Example
When to use it
- A single-page application attaches one delegated handler per route to a static shell div, covering all dynamic content injected later.
- A plugin removes only its own handlers on teardown using .off('.pluginName') without disturbing other listeners on the element.
- A drag-and-drop widget attaches mousedown.dnd, mousemove.dnd, and mouseup.dnd so they can all be removed cleanly as a group.
More examples
Delegated click on dynamic rows
The delegated handler on tbody with the .gridSelect namespace covers all future rows and can be removed cleanly later.
<table id="grid"><tbody></tbody></table>
<button id="addRow">Add row</button>
<p id="sel"></p>
<script>
$(function () {
var n = 0;
$("#addRow").click(function () {
n++;
$("#grid tbody").append("<tr><td>Row " + n + "</td></tr>");
});
$("#grid tbody").on("click.gridSelect", "tr", function () {
$("tr").removeClass("selected");
$(this).addClass("selected");
$("#sel").text($(this).find("td").text());
});
});
</script>Remove only namespaced handler
.off("click.feature") removes only the namespaced handler, leaving any other click listeners on the element intact.
<div id="box" style="padding:20px;background:#eee;">Box</div>
<button id="add">Add handler</button>
<button id="remove">Remove handler</button>
<script>
$(function () {
$("#add").click(function () {
$("#box").on("click.feature", function () {
alert("Feature handler fired!");
});
});
$("#remove").click(function () {
$("#box").off("click.feature"); // removes only this handler
});
});
</script>Grouped handlers for drag behaviour
All drag-related handlers share the .drag namespace so mousemove.drag can be removed on mouseup without a stored reference.
<div id="draggable" style="width:60px;height:60px;background:#3498db;position:absolute;cursor:grab;"></div>
<script>
$(function () {
var offsetX, offsetY;
$("#draggable")
.on("mousedown.drag", function (e) {
offsetX = e.pageX - $(this).offset().left;
offsetY = e.pageY - $(this).offset().top;
$(document).on("mousemove.drag", function (e) {
$("#draggable").css({ left: e.pageX - offsetX, top: e.pageY - offsetY });
});
})
.on("mouseup.drag", function () {
$(document).off("mousemove.drag");
});
});
</script>
Discussion