I was working on Rapua, late on a sweltering summer night while visiting family, finally tracking down the root cause of a bug I’d been studiously avoiding. The steps to reproduce were simple:

  1. Add any “content block” to a game location.
  2. Click to delete the block.
  3. Click Confirm to delete the block.

The block would then disappear with a confirmation, but only sometimes. In between steps 2 and 3, there’s an event listener on the delete button that triggers a modal, populating it with all the information required to make a valid deletion. However, sometimes this information was coming back as undefined.

Why Was It Failing?

After some digging, I discovered the culprit: sometimes the <svg> inside the button was the thing being clicked, and sometimes the click was happening on the <button> itself. Because I was using event.target in my code, I was only retrieving the exact element clicked (the <svg>), not the button’s data attributes. This caused my code to blow up since the <svg> itself didn’t have any data-* attributes.

The Gotcha

When handling events in JavaScript, you have two different properties at your disposal:

  • event.target: The element that actually initiated the event (the thing you literally clicked).
  • event.currentTarget: The element that the event listener is attached to (often the parent container or button).

As Griffin on Stack Overflow puts it:

Events bubble by default. So the difference between the two is:

  • target is the element that triggered the event (e.g., the user clicked on).
  • currentTarget is the element that the event listener is attached to.

Most of the time, if you want the data attached to the button, it’s simpler to use event.currentTarget. That way, no matter what nested elements are inside the button (icon, text, etc.), you’ll always get the button’s attributes.

The Fix

A quick fix is to swap:

function deleteBlock(event) {
  // Before: const button = event.target;
  const button = event.currentTarget;

  const locationID = button.dataset.location;
  const blockID = button.dataset.block;
  ...
}

Now, no matter where on the button the user clicks—on the text or the SVG icon—you’ll always get the correct element’s data attributes.

Takeaways

  • Use event.target when you need to know the exact nested element clicked.
  • Use event.currentTarget when you need properties, data attributes, or context from the parent element that has the listener.

This is a common gotcha that I just fell prey to while learning JS.