Skip to main content

Difference between event.target and event.currenttarget in JavaScript

event.target is the element that actually fired the event. event.currentTarget is the element that holds the event handler.

Theory

TL;DR

  • event.target is where the event started (the button you clicked inside a div).
  • event.currentTarget is where your addEventListener lives (the div itself).
  • During bubbling, target stays fixed; currentTarget changes with each handler.
  • Need to know what was clicked? Use target. Need the element with the handler? Use currentTarget.
  • For event delegation on dynamic lists, target is what you want.

Quick example

html
<div id="parent" style="padding: 20px; background: lightblue;"> <button id="child">Click me</button> </div>
javascript
const parent = document.getElementById('parent'); parent.addEventListener('click', (event) => { console.log(event.target.id); // "child" — the button was clicked console.log(event.currentTarget.id); // "parent" — handler lives here });

Click the button: target points to the button, currentTarget points to the div. The handler is on the div, but the click happened on the button inside it.

Key difference

When an event bubbles up the DOM, event.target stays locked to the original element where the event fired. event.currentTarget updates at every step and always reflects the element whose handler is currently running. So if you have nested divs with handlers on each, target is the same in all of them, but currentTarget is different in each one.

When to use

  • Dynamic children (event delegation): use target. Attach one listener to a parent <ul> and check e.target to find which <li> was clicked. Works even for items added after page load.
  • Handler's own element: use currentTarget. Toggling a class or reading dimensions of the element that owns the handler.
  • More than 10 dynamic children: delegation with target saves attaching individual listeners to each one.
  • Single element with a handler: currentTarget is predictable and needs no extra checks.

Comparison table

event.targetevent.currentTarget
Points toElement that fired the eventElement with the handler attached
During bubblingFixed (does not change)Updates at each handler
Equals this in handler?Not alwaysYes (non-arrow functions)
Use forEvent delegation, detecting sourceOperating on the handler's own element

How the browser sets these

The browser dispatches events following the DOM Level 2/3 spec: capture phase down, target phase, bubble phase up. Each time a handler runs, the engine sets currentTarget to the element that registered that specific listener. target is set once at dispatch and never changed again. That is why after stopPropagation(), target is still the original element.

Common mistakes

Mistake: styling target when you mean the handler's element

javascript
// Handler on div; user clicks a nested img inside the div elem.addEventListener('click', (e) => { e.target.style.background = 'red'; // Paints the img, not the div });

Fix: e.currentTarget.style.background = 'red'.

Mistake: toggling a class on target without a guard

javascript
parent.addEventListener('click', (e) => { e.target.classList.toggle('active'); // Toggles on any child: span, p, img... });

Fix: add a check first. if (e.target.matches('button')) e.currentTarget.classList.toggle('active').

Mistake: assuming target === currentTarget always

They are only equal when you click directly on the element that holds the handler. The moment a nested child is clicked, they diverge. I have seen this break tooltip close-handlers where clicking a span inside a tooltip wrapper closes the tooltip itself, because the handler checked e.target instead of e.currentTarget.

Real-world usage

  • React: SyntheticEvent.target for form delegation (reading e.target.value in a shared onChange). currentTarget for accessing the form element itself.
  • Vue: $event.target inside v-on for dynamic slot content.
  • Vanilla delegation: one listener on a table, e.target.closest('tr') to get the clicked row.
  • jQuery migration: $(event.target).closest() patterns map directly to e.target.closest() in vanilla.

Follow-up questions

Q: What happens to target and currentTarget in the capture phase?
A: Both are set during capture too. currentTarget matches the capturing element; target is still the original dispatcher.

Q: Does stopPropagation() change target?
A: No. target is fixed at dispatch. stopPropagation() only stops the event from moving to the next element in the chain.

Q: In a non-arrow handler function, what does this equal?
A: this equals currentTarget. That is why older code used this instead of event.currentTarget. Arrow functions break this because they do not bind their own this.

Q: What if the clicked element is the same as the handler's element?
A: Then target === currentTarget. This happens when you click directly on the element you attached the listener to, with no child elements in between.

Q: In React Portals, where does target point?
A: target points to the portal child. Even though the Portal renders outside the parent DOM tree, React's synthetic event system re-dispatches through the React component tree, so delegation still works across the portal boundary.

Examples

Event delegation on a dynamic list

javascript
const list = document.getElementById('list'); // One listener handles all items, including ones added later list.addEventListener('click', (e) => { if (e.target.tagName === 'LI') { console.log('Clicked:', e.target.textContent); e.target.classList.toggle('selected'); } // e.currentTarget is always the #list element }); // This new item works immediately — no extra listener needed const newItem = document.createElement('li'); newItem.textContent = 'New item'; list.appendChild(newItem);

One handler on the parent covers all items, current and future. e.target tells you which specific item was clicked; e.currentTarget stays locked to the list.

Form with a shared handler using both properties

javascript
const form = document.getElementById('signup-form'); form.addEventListener('change', (e) => { const field = e.target; // Which input changed const formEl = e.currentTarget; // The form element itself console.log('Field name:', field.name); console.log('New value:', field.value); // Re-validate the whole form using the form element const allInputs = formEl.querySelectorAll('input'); const allFilled = [...allInputs].every(i => i.value.trim() !== ''); formEl.querySelector('#submit').disabled = !allFilled; });

e.target identifies which field changed. e.currentTarget gives access to the whole form for re-validation. Both properties doing different jobs inside the same handler.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?