Can anyone please tell me the exact difference between currentTarget and target property in JavaScript events with example and which property is used in which scenario?
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.
target = element that triggered event.
currentTarget = element that has the event listener.
Minimal runnable example
window.onload = function() {
var resultElem = document.getElementById('result')
document.getElementById('1').addEventListener(
'click',
function(event) {
resultElem.innerHTML += ('<div>target: ' + event.target.id + '</div>')
resultElem.innerHTML += ('<div>currentTarget: ' + event.currentTarget.id + '</div>')
},
false
)
document.getElementById('2').dispatchEvent(
new Event('click', { bubbles:true }))
}
<div id="1">1 click me
<div id="2">2 click me as well</div>
</div>
<div id="result">
<div>result:</div>
</div>
If you click on:
2 click me as well
then 1 listens to it, and appends to the result:
target: 2
currentTarget: 1
because in that case:
2 is the element that originated the event
1 is the element that listened to the event
If you click on:
1 click me
instead, the result is:
target: 1
currentTarget: 1
Tested on Chromium 71.
For events whose bubbles property is true, they bubble.
Most events do bubble, except several, namely focus, blur, mouseenter, mouseleave, ...
If an event evt bubbles, the evt.currentTarget is changed to the current target in its bubbling path, while the evt.target keeps the same value as the original target which triggered the event.
It is worth noting that if your event handler (of an event that bubbles) is asynchronous and the handler uses evt.currentTarget. currentTarget should be cached locally because the event object is reused in the bubbling chain (codepen).
const clickHandler = evt => {
const {currentTarget} = evt // cache property locally
setTimeout(() => {
console.log('evt.currentTarget changed', evt.currentTarget !== currentTarget)
}, 3000)
}
If you use React, from v17, react drops the Event Pooling.
Therefore, the event object is refreshed in the handler and can be safe to use in asynchronous calls (codepen).
↑is not always true. onClick event's currentTarget is undefined after the event handler finishes. In conclusion, always cache the event's properties locally if you are going to use them after a synchronous call.
From react docs
Note:
As of v17, e.persist() doesn’t do anything because the SyntheticEvent
is no longer pooled.
And many other things that are too long to be pasted in an answer, so I summarized and made a blog post here.
If this isn't sticking, try this:
current in currentTarget refers to the present. It's the most recent target that caught the event that bubbled up from elsewhere.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
If click on the P tag in above code then you will get three alert,and if you click on the div tag you will get two alert and a single alert on clicking the form tag.
And now see the following code,
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<script>
function fun(event){
alert(event.target+" "+event.currentTarget);
}
</script>
<form>FORM
<div onclick="fun(event)">DIV
<p>P</p>
</div>
</form>
We just removed onclick from the P and form tag and now when we click we on P tag we get only one alert:
[object HTMLParagraphElement] [object HTMLDivElement]
Here event.target is [object HTMLParagraphElement],and event.curentTarget is [object HTMLDivElement]:
So
event.target is the node from which the event originated,
and
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached.To know more see bubbling
Here we clicked on P tag but we don't have listener on P but on its parent element div.
Event.currentTarget is the element to which the event handler has been
attached, as opposed to Event.target, which identifies the element on
which the event occurred and which may be its descendant.
Source: MDN
target always refers to the element in front of addEventListener - it's the element on which the event originated.
currentTarget tells you - if this is an event that's bubbling - the element that currently has the event listener attached (which will fire the event handler if the event occurs).
See this CodePen for an example. If you open up developer tools and click the square, you'll see that first the div is the target and the currentTarget, but the event bubbles up to the main element - then main element becomes the currentTarget, while the div is still the target. Note the event listener needs to be attached to both elements for the bubbling to occur.
event.target is the node from which the event originated, ie. wherever you place your event listener (on paragraph or span), event.target refers to node (where user clicked).
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached. Ie. if we attached our event listener on paragraph node, then event.currentTarget refers to paragraph while event.target still refers to span.
Note: that if we also have an event listener on body, then for this event-listener, event.currentTarget refers to body (ie. event provided as input to event-listerners is updated each time event is bubbling one node up).
Here's a simple scenario to explain why it's needed. Let's say there are some messages that you show to the user with the format below, but you also want to give them the freedom to close them (unless you have a special mental disorder), so here are some message panes:
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
and when the user clicks on the [x] button on each, the whole corresponding pane must be removed.
Here's the HTML code for the pane:
<div class="pane">
A message will be here
<span class="remove-button">[x]</span>
</div>
Now where do you want to add the click event listener? The user clicks on [x], but you want to remove the pane, so:
If you add the click event listener to the [x], then you will have to find its parent on DOM and remove it... which is possible but ugly and "DOM dependent".
And if you add the click event listener to the pane, clicking "everywhere on the pane" will remove it, and not just clicking on its [x] button.
So what can we do? We can use the "Bubbles Up" feature of the event system:
"Events are raised and bubble up the DOM tree regardless of the existence of any event handlers."
In our example, this means that even if we add the event handlers to the panes, we will be able to catch the events raised specifically by the [x] button clicks (because events bubble up). So there can be difference between where an event is raised, and where we catch and handle it.
Where it's raised will be in the event.target, and where it's caught will be in the event.currentTarget (where we're currently handling it). So:
let panes = document.getElementsByClassName("pane");
for(let pane of panes){
pane.addEventListener('click', hndlr);
}
function hndlr(e){
if(e.target.classList.contains('remove-button')){
e.currentTarget.remove();
}
}
(The credit of this example goes to the website JavaScript.info)
An experiment:
document.addEventListener("click", (e) => {
console.log(e.target, e.currentTarget);
});
document.querySelector("p").click();
output:
<p></p>
#document
The target (<p></p>) seems to be the element clicked, while the currentTarget (#document) is the element that is listening for the click event.
Related
I'm having trouble understanding when/how event listeners are added and have tried to illustrate my general question through this little snippet.
If the toggle button has a mousedown event, then the little box toggles between being displayed or not displayed. Also, if displayed then for a mousedown event anywhere in the document the box will change to not displayed.
My question is why won't it work if evt.stopPropagation is commented out?
If both .evt.stopPropagation and the document.addEventListener are commented out then the toggle works as expected, but only for the button, and the box is not removed from display for mousedown on the document.
If a console.log message is placed inside the clear function when the event listener on the document is active and evt.stopPropagation is commented out, it can be seen that the mousedown on the button also triggers the mousedown on the document. So, clicking the toggle button displays and removes from display at the same time, and the box is never seen.
I was expecting the button event to add the event listener on the document after its own event, such that the document couldn't register a mousedown event for the first event on the button because it wasn't yet declared; but it appears that both listeners are set up at the same time.
Could you please explain? Thank you for considering my question.
"use strict";
document.querySelector('button').addEventListener( 'mousedown', display, false );
function display( evt )
{
evt.stopPropagation();
let e = evt.currentTarget.nextElementSibling;
if ( e.style.display === 'block' )
{
e.style.display = 'none';
}
else
{
e.style.display = 'block';
document.addEventListener( 'mousedown', clear, false );
}; // end if
} // close display
function clear()
{
document.removeEventListener( 'mousedown', clear, false );
document.querySelector('button').nextElementSibling.style.display = 'none';
}
div {
display: none;
background-color: rgb(150,150,150);
border: 1px solid black;
height: 50px;
width: 50px;
margin-top: 10px;
}
<button>Toggle</button>
<div></div>
#traktor53's answer correctly identified what happens, but I fear their explanation is not clear enough.
What you are experiencing here is basically caused by two facts:
MouseEvents do bubble to their ancestors.
EventTarget.addEventListener is a synchronous method.
To understand better, I will avoid speaking about the capture phase for now.
So when a browser is to dispatch an Event on a target, it will first check all the handlers it has to call on that target, then execute all these handlers, and finally bubble up the DOM to the window (of course, only in case of bubbling event).
Taking your example, we can schematize this bubbling phase like so:
[<button>] -> list of handlers: [`ƒ display`]
execute `ƒ display` // (add handler on document)
continue with parentNode
[<body>] -> list of handlers: none
continue with parentNode
[<html>] -> list of handlers: none
continue with ownerDocument
[document] -> list of handlers: [`ƒ clear`] // (added in `display`)
execute `ƒ clear`
continue with defaultView
[window] -> list of handlers: none
As you can see, the EventHandler you add on document is added before the algorithm got to check what are the EventHandlers attached to document. So when the Event is to fire on the document EventTarget, the latter will have this EventHandler attached to itself.
To demonstrate it, we can even build this ladder code, which will add an EventListener to all the ancestors of the original target from inside the first EventHandler:
document.querySelector('button').addEventListener('mousedown', handle, {once:true});
function handle(evt) {
console.log('firing from', this.toString());
const up = this.defaultView || // window for document
this.parentNode || // elements until html
this.ownerDocument; // html
if(up) up.addEventListener('mousedown', handle, {once:true});
}
<button>click me</button>
Doing so, your clear function will get called right after display got called, reverting immediately what display had done.
In the bubbling phase, events bubble up the DOM from the target element which generated the event, via the chain of elements' parent nodes, to the document object and from there to window:
Source 3.1 figure
Calling event.stopPropagation in a handler stops the event bubbling any further up the DOM (of course).
Now if the next sibling of the toggle is not on display, the display event handler stops propagation, shows the next element and registers a listener on the document node to hide the element again.
If stopPropagation is not called, the mousedown event continues to bubble up the DOM looking for "mousedown" listeners to call. It finds the one added by display to the document node ( i.e. the 'clear` function), calls it, and handler execution hides the next element after the toggle.
You never get to see the next element in the brief period between calling the mousedown handler for button and that for document, and if processing the event bubbling blocks screen update, you never will.
It is perfectly clear that event.target deals with the DOM element that initiated the event.
And - event.delegateTarget provides the DOM element where we actually attached the listener ,
But I'm having trouble to understand when would I use - event.currentTarget :
Looking at jQuery samples which shows this example : JSBIN1
It seems like it is the exact same as event.delegateTarget
$( "body" ).click(function( event ) {
$( "#log" ).html( "clicked: " + event.currentTarget.nodeName );
});
Everywhere I click - it says : clicked "BODY" - which is exactly like the delegateTarget's behaviour
Question
In Which (real life) scenarios would I use event.currentTarget? ( example would be much appreciated)
nb - couldn't find any currentTarget vs delegateTarget questions....
event.target and event.currentTarget are attributes of Event interface defined by W3C spec:
event.target:
This property of event objects is the object the event was dispatched
on. It is different than event.currentTarget when the event handler is
called in bubbling or capturing phase of the event.
event.currentTarget:
Identifies the current target for the event, as the event traverses
the DOM. It always refers to the element the event handler has been
attached to as opposed to event.target which identifies the element on
which the event occurred.
Additionally, jQuery adds event.delegateTarget:
The element where the currently-called jQuery event handler was attached
The difference with event.currentTarget is explained in
This property is most often useful in delegated events attached by
.delegate() or .on(), where the event handler is attached at an
ancestor of the element being processed. It can be used, for example,
to identify and remove event handlers at the delegation point.
For non-delegated event handlers attached directly to an element,
event.delegateTarget will always be equal to event.currentTarget.
For example, if you click the button in the following HTML:
<div class="box">
<button>Button</button>
</div>
$( "body" ).on( "click", ".box", function(e) {
e.delegateTarget; // body
e.currentTarget; // .box
e.target; // button
});
If you have HTML markup as shown below
<a href="http://www.someurl.com" data-ajax="true" data-id="345">
<img src="company.jpg"/>
</a>
And you have bind handler to anchor
$("a").click(function(){
// Some stuff
});
Now here in the click handler event.target points to the image but you have actually clicked on anchor and in the click handler you need to access the data-id attribute of anchor to perform some action. here to get the current target you can use event.currentTarget to access the anchor.
The event.currentTarget property is the element that is currently responding to the DOM event, which is the element you bound the event handler to. As for a real world usage? Not really sure.
I don't understand the difference, they both seem the same but I guess they are not.
Any examples of when to use one or the other would be appreciated.
e.target is what triggers the event dispatcher to trigger and e.currentTarget is what you assigned your listener to.
Ben is completely correct in his answer - so keep what he says in mind. What I'm about to tell you isn't a full explanation, but it's a very easy way to remember how e.target, e.currentTarget work in relation to mouse events and the display list:
e.target = The thing under the mouse (as ben says... the thing that triggers the event).
e.currentTarget = The thing before the dot... (see below)
So if you have 10 buttons inside a clip with an instance name of "btns" and you do:
btns.addEventListener(MouseEvent.MOUSE_OVER, onOver);
// btns = the thing before the dot of an addEventListener call
function onOver(e:MouseEvent):void{
trace(e.target.name, e.currentTarget.name);
}
e.target will be one of the 10 buttons and e.currentTarget will always be the "btns" clip.
It's worth noting that if you changed the MouseEvent to a ROLL_OVER or set the property btns.mouseChildren to false, e.target and e.currentTarget will both always be "btns".
I like visual answers.
When you click #btn, two event handlers get called and they output what you see in the picture.
Demo here: https://jsfiddle.net/ujhe1key/
e.currentTarget is always the element the event is actually bound do. e.target is the element the event originated from, so e.target could be a child of e.currentTarget, or e.target could be === e.currentTarget, depending on how your markup is structured.
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.
It's worth noting that event.target can be useful, for example, for using a single listener to trigger different actions. Let's say you have the typical "menu" sprite with 10 buttons inside, so instead of doing:
menu.button1.addEventListener(MouseEvent.CLICK, doAction1);
menu.button2.addEventListener(MouseEvent.CLICK, doAction2);
etc...
You can simply do:
menu.addEventListener(MouseEvent.CLICK, doAction);
And trigger a different action within doAction(event) depending on the event.target (using it's name property, etc...)
make an example:
var body = document.body,
btn = document.getElementById( 'id' );
body.addEventListener( 'click', function( event ) {
console.log( event.currentTarget === body );
console.log( event.target === btn );
}, false );
when you click 'btn', and 'true' and 'true' will be appeared!
e.currentTarget would always return the component onto which the event listener is added.
On the other hand, e.target can be the component itself or any direct child or grand child or grand-grand-child and so on who received the event. In other words, e.target returns the component which is on top in the Display List hierarchy and must be in the child hierarchy or the component itself.
One use can be when you have several Image in Canvas and you want to drag Images inside the component but Canvas. You can add a listener on Canvas and in that listener you can write the following code to make sure that Canvas wouldn't get dragged.
function dragImageOnly(e:MouseEvent):void
{
if(e.target==e.currentTarget)
{
return;
}
else
{
Image(e.target).startDrag();
}
}
e.target is element, which you f.e. click
e.currentTarget is element with added event listener.
If you click on child element of button, its better to use currentTarget to detect buttons attributes, in CH its sometimes problem to use e.target.
e.currentTarget is element(parent) where event is registered, e.target is node(children) where event is pointing to.
I need to click on a document to call some function, but the problem is that when I click on some element that want it doesnt react, so the code:
<body>
<div class="some_element">
some element
</div>
</body>
and js:
$(document).click(function(){
//something to happen
})
and now if I click on the div with class="some_element" the document.click event will be called, but I need to call that event ONLY when I click on the document; or it is possible the make this element an exception?
More detailed:
$('#forma').click(function(e){
e.stopPropagation();
$('#assignment_type_list').slideUp();
})
Lets say #forma - its a parent element of those element, so when I click on the page I want to slideUp someElement and:
$('#assignment_type_select, #assignment_type_label').click(function(){
$('#assignment_type_list').slideToggle();
})
this is the elements when they are clicked the other element is toggled, but the problem is that when I click on this elements the $('#forma').click - also executes, because its parent and the e.stopPropagation() - doesn't help.
All this stopPropagation stuff is right, though this'll cause your script to throw errors on older versions of a certain browser. Guess which one? a cross-browser way:
$('#elem').click(function(e)
{
e = e || window.event;//IE doesn't pass the event object as standard to the handler
//event would normally work, but if you declared some event variable in the current scope
//all falls to pieces, so this e || window.event; thing is to be preferred (IMO)
if (e.stopPropagation)//check if method exists
{
e.stopPropagation();
return;
}
e.cancelBubble = true;//for IE
});
However, you wanted to check if the element that was actually clicked, is the one you need. The problem with that is, that the way the event is passed through the DOM. In W3C browsers the event is first passed to the document, and then clambers down to the element that was actually clicked (propagates through the dom). By contrast IE dispatches its events on the element itself, and then sends it up to the document (except for the change event triggered by select elements... to add insult to injury). What this effectively means is that a click event that is registered in to body element in W3C browsers might be on its way to a checkbox of sorts, or it could be a click inside an empty div. Again, in IE, when a click event reaches the body tag, it could have been dispatched too any element on the page. So it may prove useful in your case to google: event delegation, or turn to jQuery's .delegate() method.
Or check the event object to see if the event is allowed to propagate through or not:
var target = e.target || e.srcElement;//target now holds a reference to the clicked element
The property names neatly show the difference between the bubbling model and the propagating one: in the first case (srcElement), the event is coming from a source element in the dom. In the W3C propagating model, the event is cought while it's headed for a target element somewhere in the dom. Look at it like a heat-seeking missile (w3c) versus a shower of debris after the target was shot down (IE, always the destructive one, and in this case often to late to respond to the events, and therefore to late to handle them:P)
One way to do it is to check for the event's target.
$('html').click(function(event){
if (event.target != this){
}else{
//do stuff
}
});
Here's a working fiddle
Elements on the document are part of the document, so if you click "some_element" in the document, it is obvious that event registered on document will be fired/triggered. If you dont want to execute code which was for "document" then first get the element OR "event source" which originates this event, and check if it was "some_element" in your question above.
Can anyone please tell me the exact difference between currentTarget and target property in JavaScript events with example and which property is used in which scenario?
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.
target = element that triggered event.
currentTarget = element that has the event listener.
Minimal runnable example
window.onload = function() {
var resultElem = document.getElementById('result')
document.getElementById('1').addEventListener(
'click',
function(event) {
resultElem.innerHTML += ('<div>target: ' + event.target.id + '</div>')
resultElem.innerHTML += ('<div>currentTarget: ' + event.currentTarget.id + '</div>')
},
false
)
document.getElementById('2').dispatchEvent(
new Event('click', { bubbles:true }))
}
<div id="1">1 click me
<div id="2">2 click me as well</div>
</div>
<div id="result">
<div>result:</div>
</div>
If you click on:
2 click me as well
then 1 listens to it, and appends to the result:
target: 2
currentTarget: 1
because in that case:
2 is the element that originated the event
1 is the element that listened to the event
If you click on:
1 click me
instead, the result is:
target: 1
currentTarget: 1
Tested on Chromium 71.
For events whose bubbles property is true, they bubble.
Most events do bubble, except several, namely focus, blur, mouseenter, mouseleave, ...
If an event evt bubbles, the evt.currentTarget is changed to the current target in its bubbling path, while the evt.target keeps the same value as the original target which triggered the event.
It is worth noting that if your event handler (of an event that bubbles) is asynchronous and the handler uses evt.currentTarget. currentTarget should be cached locally because the event object is reused in the bubbling chain (codepen).
const clickHandler = evt => {
const {currentTarget} = evt // cache property locally
setTimeout(() => {
console.log('evt.currentTarget changed', evt.currentTarget !== currentTarget)
}, 3000)
}
If you use React, from v17, react drops the Event Pooling.
Therefore, the event object is refreshed in the handler and can be safe to use in asynchronous calls (codepen).
↑is not always true. onClick event's currentTarget is undefined after the event handler finishes. In conclusion, always cache the event's properties locally if you are going to use them after a synchronous call.
From react docs
Note:
As of v17, e.persist() doesn’t do anything because the SyntheticEvent
is no longer pooled.
And many other things that are too long to be pasted in an answer, so I summarized and made a blog post here.
If this isn't sticking, try this:
current in currentTarget refers to the present. It's the most recent target that caught the event that bubbled up from elsewhere.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
If click on the P tag in above code then you will get three alert,and if you click on the div tag you will get two alert and a single alert on clicking the form tag.
And now see the following code,
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<script>
function fun(event){
alert(event.target+" "+event.currentTarget);
}
</script>
<form>FORM
<div onclick="fun(event)">DIV
<p>P</p>
</div>
</form>
We just removed onclick from the P and form tag and now when we click we on P tag we get only one alert:
[object HTMLParagraphElement] [object HTMLDivElement]
Here event.target is [object HTMLParagraphElement],and event.curentTarget is [object HTMLDivElement]:
So
event.target is the node from which the event originated,
and
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached.To know more see bubbling
Here we clicked on P tag but we don't have listener on P but on its parent element div.
Event.currentTarget is the element to which the event handler has been
attached, as opposed to Event.target, which identifies the element on
which the event occurred and which may be its descendant.
Source: MDN
target always refers to the element in front of addEventListener - it's the element on which the event originated.
currentTarget tells you - if this is an event that's bubbling - the element that currently has the event listener attached (which will fire the event handler if the event occurs).
See this CodePen for an example. If you open up developer tools and click the square, you'll see that first the div is the target and the currentTarget, but the event bubbles up to the main element - then main element becomes the currentTarget, while the div is still the target. Note the event listener needs to be attached to both elements for the bubbling to occur.
event.target is the node from which the event originated, ie. wherever you place your event listener (on paragraph or span), event.target refers to node (where user clicked).
event.currentTarget, on the opposite, refers to the node on which current-event listener was attached. Ie. if we attached our event listener on paragraph node, then event.currentTarget refers to paragraph while event.target still refers to span.
Note: that if we also have an event listener on body, then for this event-listener, event.currentTarget refers to body (ie. event provided as input to event-listerners is updated each time event is bubbling one node up).
Here's a simple scenario to explain why it's needed. Let's say there are some messages that you show to the user with the format below, but you also want to give them the freedom to close them (unless you have a special mental disorder), so here are some message panes:
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
[ A message will be in this pane [x] ]
and when the user clicks on the [x] button on each, the whole corresponding pane must be removed.
Here's the HTML code for the pane:
<div class="pane">
A message will be here
<span class="remove-button">[x]</span>
</div>
Now where do you want to add the click event listener? The user clicks on [x], but you want to remove the pane, so:
If you add the click event listener to the [x], then you will have to find its parent on DOM and remove it... which is possible but ugly and "DOM dependent".
And if you add the click event listener to the pane, clicking "everywhere on the pane" will remove it, and not just clicking on its [x] button.
So what can we do? We can use the "Bubbles Up" feature of the event system:
"Events are raised and bubble up the DOM tree regardless of the existence of any event handlers."
In our example, this means that even if we add the event handlers to the panes, we will be able to catch the events raised specifically by the [x] button clicks (because events bubble up). So there can be difference between where an event is raised, and where we catch and handle it.
Where it's raised will be in the event.target, and where it's caught will be in the event.currentTarget (where we're currently handling it). So:
let panes = document.getElementsByClassName("pane");
for(let pane of panes){
pane.addEventListener('click', hndlr);
}
function hndlr(e){
if(e.target.classList.contains('remove-button')){
e.currentTarget.remove();
}
}
(The credit of this example goes to the website JavaScript.info)
An experiment:
document.addEventListener("click", (e) => {
console.log(e.target, e.currentTarget);
});
document.querySelector("p").click();
output:
<p></p>
#document
The target (<p></p>) seems to be the element clicked, while the currentTarget (#document) is the element that is listening for the click event.