Makes entire elements clickable by delegating clicks to a clickable element within. Works with anchor links, buttons, or any other clickable element. Useful for layouts where you want a large clickable area while still keeping a single, explicit primary target in complex card markup.
Handles text selection (no accidental clicks), Ctrl+click and middle-click on links (opens in new tab), touch and pointer devices, and dynamically added items via MutationObserver.
npm install click-delegation
Or include directly via a <script> tag:
<script src="clickDelegation.min.js"></script>Add data-delegate to any item and data-delegate-to to the clickable element inside it, then call clickDelegation():
<div data-delegate>
<h2><a href="/page" data-delegate-to>Title</a></h2>
<p>Clicking anywhere on this item navigates to /page.</p>
</div>
<script src="clickDelegation.min.js"></script>
<script>clickDelegation();</script>Works with buttons too:
<div data-delegate>
<h2>Card title</h2>
<button data-delegate-to>Open modal</button>
</div>The item automatically receives the class is-clickable, which you can use to style it:
.is-clickable {
cursor: pointer;
}All options are optional. Defaults shown below:
const instance = clickDelegation({
parent: 'data-delegate', // attribute on the clickable item
target: 'data-delegate-to', // attribute on the target element
ignore: 'data-delegate-ignore', // attribute to exclude child elements
clickableClass: 'is-clickable', // class added to clickable items
downUpTime: 200, // max ms between pointerdown/up to count as a click
onClick: null // callback fired on click: (item, target) => {}
});clickDelegation() returns an instance with a destroy() method that removes all event listeners, disconnects the MutationObserver and removes clickableClass from all items. Useful in SPAs or when switching configurations.
const instance = clickDelegation();
// Later:
instance.destroy();Use onClick to run custom logic when an item is clicked — useful for analytics or state updates. The callback fires before the target element's click and cannot cancel it:
clickDelegation({
onClick(item, target) {
console.log('Clicked', target);
}
});If an item contains multiple clickable elements, use a named reference to specify which one should act as the primary target:
<div data-delegate="primary">
<h2><a href="/page" data-delegate-to="primary">Title</a></h2>
<a href="/other">Other link</a>
</div>Add data-delegate-ignore to any element inside an item that should not trigger a click:
<div data-delegate>
<a href="/page" data-delegate-to>Title</a>
<button data-delegate-ignore>Add to favourites</button>
</div>Buttons and anchor tags are always ignored as click sources automatically (so they retain their own independent behaviour), unless they carry the data-delegate-to attribute themselves.
- Keyboard accessibility — keyboard users interact with the inner target element directly; the library does not add keyboard activation to the container element.
- Touch & pointer support — uses
pointerdown/pointerupso mouse, touch and stylus all work. - Text selection — clicking and dragging to select text does not trigger a click (threshold: 200ms).
- Ctrl/Meta+click / middle-click — opens anchor links in a new tab with
noopener,noreferrer. Non-anchor targets (e.g. buttons) receive a regularclick(). - Right-click — ignored, so the browser context menu works as expected.
- Script in
<head>— safe to include before<body>exists; initialisation is deferred toDOMContentLoaded. - Dynamic content — items added to the DOM after page load are handled automatically via
MutationObserver. - Attribute changes — adding or removing
data-delegateordata-delegate-toon existing elements is detected automatically.
MIT