Event delegation lets you attach a single listener to a parent element and handle interactions for matching descendants as events bubble up through the DOM. This is useful when a container has many interactive children, such as action lists, menus, tables, or todo items, because it avoids attaching one listener per child. It also works well when matching elements are added or removed dynamically, since the parent listener can continue handling future descendants without needing to be rewired.
Implement delegateEvent(container, eventName, selector, listener).
The utility should attach one listener to container and invoke listener(event, matchedElement) whenever an event of type eventName originates from:
selector.selector.const actions = document.querySelector('#actions');const cleanup = delegateEvent(actions,'click','button[data-action]',(event, matchedElement) => {console.log(matchedElement.getAttribute('data-action'));},);// Clicking this nested <span>:// <button data-action="delete"><span>Delete</span></button>// should log: "delete"cleanup();
delegateEvent(container, eventName, selector, listener)| Parameter | Type | Description |
|---|---|---|
container | Element | The parent element that receives the delegated listener. |
eventName | string | The event type to listen for, such as 'click'. |
selector | string | The CSS selector used to match descendant elements. |
listener | (event: Event, matchedElement: Element) => void | The callback invoked with the original event and the nearest matching descendant. |
Returns a cleanup function that removes the delegated listener from container.
container should trigger listener.container itself matches selector, direct events on container should still be ignored.event.target to determine the matching descendant. A closest()-style lookup is the intended technique.console.log() statements will appear here.