What is event delegation, and why is it useful?
Event delegation means putting a single listener on a shared ancestor instead of one listener on every element you care about. The browser does the dispatch for you: the event bubbles up from the element that was actually clicked to the ancestor, and the ancestor's handler runs.
There are two reasons it is worth the extra step. The first is performance. If a list can have a thousand rows, one listener on the container is a thousand times cheaper to register than a thousand listeners on the rows, and it stays that way as the list grows. The second is that it survives changes to the markup. Rows added later work automatically, and rows removed take their listeners with them, so there are no stale handlers pointing at elements that no longer exist.
That second point is the bigger win in practice. The alternative, attaching a listener to every row and removing it when the row is removed, is where memory leaks come from in single page applications.
The cost is that the handler has to work out which element the user actually interacted with. The event object gives you the target, which is the deepest element the event happened on, so clicking the text inside a button reports the text node's parent rather than the button. The standard technique is to use the closest method with a selector, which walks up from the target to the first ancestor that matches, and to check that the match is inside the container you delegated from.