2

In some class in constructor I have method that is attached to click event listener. this.mapButton.addEventListener('click', this.expandOrCollapseMap.bind(this)); The responsibility of that method is to download map and expand it on the view.

expandOrCollapseMap(eventArgs) {
    // eventArgs.currentTarget != null
    loadGoogleMapsApi({
        key: '{{GOOGLE_MAPS.API_KEY}}',
    }).then((googleMaps)=>{
        if(!this.map) {
            this.mapAPI = googleMaps;
            this.map = new googleMaps.Map(this.mapContainer, {
                zoom: 12,
                center: {
                    lat: 40.722216,
                    lng: -73.987501
                },
                scrollwheel: false
            });
        }
    }).then(()=>{
        // eventArgs.currentTarget is null
        let clickedButton = eventArgs.currentTarget;
        this.changeMapIcon(clickedButton);
        this.changeMapHeight();
        this.addMarkersToMap(this.restaurants);
        this.expanded = !this.expanded;
    })
    .catch((err)=>{
        this.map = null;
        console.log("Error while loading map: ", err);
    });
}

My question is why when I debug in Chrome outside promise then I can access eventArgs.currentTarget, while inside then callback eventArgs.currentTarget is null, but other properties of eventArgs can be accessed?

lissajous
  • 371
  • 5
  • 17

2 Answers2

3

The event is shared across all elements it bubbles to. That means every element that shares the event will receive the same instance of the event object. Why is it important?

According to MDN

(event.currentTarget) Identifies the current target for the event, as the event traverses the DOM. It always refers to the element to which the event handler has been attached, as opposed to event.target which identifies the element on which the event occurred.

Summing up: event.currentTarget is a transient property on the object that is the same for all the event's subscribers; that means that after your event listener is finished, the currentTarget will be set to the next event listener's element until there will be none found and currentTarget will be (correctly) set to null.

See the described example below:

document.getElementById('btnnnn').addEventListener('click', e => {
  e.test = 'test'; // we set here the property to prove that event object is shared with body

  // now we'll print two similar properties that are in fact totally different in all senses
  console.log('inside event', e.target, e.currentTarget);

  // and now we delay the same thing; it is going to be executed after all event listeners
  setTimeout(() => console.log('after all event listeners', e.target, e.currentTarget), 0);
});

// check that it's the same object
document.body.addEventListener('click', e => console.log('body, same object?', e.test));
<button id="btnnnn">Click me</button>

And finally here is the specification that at point 7 clearly instructs to set currentTarget to null after event has completed its cycle.

smnbbrv
  • 23,502
  • 9
  • 78
  • 109
1

This is due to event bubbling. The very same event object will be fired on different elements, and will have currentEventTarget set to the current element before the listeners installed on that element are fired. As soon as the event reaches the document root, the currentEventTarget is set to null. So if you are observing it asynchronously (in the promise callback), that's the value you will get.

The workaround is trivial: Just put the let clickedButton = eventArgs.currentTarget; at the top of the function.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375