Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

event currentTarget changes after setTimeout

consider:

let sel=document.getElementById('mys');

sel.onchange=function(e) {
  console.log(e.currentTarget===null); // false
  setTimeout(e => {
     console.log(e.currentTarget===null); // true
  }, 0, e);
 }
<select id="mys">
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="mercedes">Mercedes</option>
  <option value="audi">Audi</option>
</select>
  • why does e.currentTarget changes after the timeout ? is that a browser (chrome) bug ?

  • how can I transfer an exact clone of the event to the timeout function ? I tried simple cloning but currentTarget is not writable and cannot be ovverridden ..

like image 998
kofifus Avatar asked Sep 22 '16 21:09

kofifus


People also ask

What is the difference between event target and event currentTarget?

e. target is what triggers the event dispatcher to trigger and e. currentTarget is what you assigned your listener to. Save this answer.

Can I use event currentTarget?

currentTarget is only available while the event is being handled. If you console. log() the event object, storing it in a variable, and then look for the currentTarget key in the console, its value will be null . Instead, you should either use console.

What does currentTarget do in Javascript?

The currentTarget event property returns the element whose event listeners triggered the event. This is particularly useful during capturing and bubbling.


2 Answers

event.currentTarget and some other properties change after exiting the event handler.

Doing a context switch (setTimeout(... ,0);) in an event handler is common, it seems that the only way to correctly pass the event including currentTarget etc is to clone it as below.

Note that this is only 'good enough' ... the clone is not exactly the original event, and it's context is different ... doing for example clone.stopPropogation() is meaningless ...

If you need to change currentTarget etc on the clone delete !d.writable || !d.configurable || !d.enumerable ||

let sel=document.getElementById('mys');

function cloneEvent(e) {
	function ClonedEvent() {};
	let clone=new ClonedEvent();
	for (let p in e) {
		let d=Object.getOwnPropertyDescriptor(e, p);
		if (d && (!d.writable || !d.configurable || !d.enumerable || d.get || d.set)) {
			Object.defineProperty(clone, p, d);
		}
		else {
			clone[p] = e[p];
		}  
	}
	Object.setPrototypeOf(clone, e);
	return clone;
}

sel.onchange=function(e) {
	console.log(e.currentTarget);
	let clone=cloneEvent(e);
	setTimeout(e => {
		console.log(e.currentTarget);
	}, 0, clone);
}
<select id="mys">
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="mercedes">Mercedes</option>
</select>
like image 170
kofifus Avatar answered Sep 29 '22 23:09

kofifus


Inspired by Joseph Marikle's comment, I found the real cause:

DOM Standard § 2.9. Dispatching events describes the algorithm, which I'm partially reproducing here:

To **dispatch** an event to a target […] run these steps:
[…]
5. If target is not relatedTarget or target is event’s relatedTarget, then:
    […]
    9. While parent is non-null:
        […]
        8. Otherwise, set target to parent and then:
            […]
        9. If parent is non-null, then set parent to the result of invoking parent’s get the parent with event.
        […]
    […]
    12. Set event’s eventPhase attribute to CAPTURING_PHASE.
    […]
    14. For each tuple in event’s path, in reverse order:
        1. […] invoke […]
    15. For each tuple in event’s path, in order:
        1. If tuple’s target is non-null, then set event’s eventPhase attribute to AT_TARGET.
        2. Otherwise, set event’s eventPhase attribute to BUBBLING_PHASE.
        3. […] invoke […]
[…]
6. Set event’s eventPhase attribute to NONE.
7. Set event’s currentTarget attribute to null.
[…]

To **invoke** […] run these steps:
[…]
5. Initialize event’s currentTarget attribute to struct’s invocation target.
[…]

So, after the event has finished processing all phases (capture, at_target, bubbling), then currentTarget gets assigned null.

like image 36
Denilson Sá Maia Avatar answered Sep 30 '22 01:09

Denilson Sá Maia