5

HTML

<div class="sym-box" data-card="0">
    <div id="a"><img src="..."></div>
</div>
<div class="sym-box" data-card="1">
    <div id="b"><img src="..."></div>
</div>
<div class="sym-box" data-card="2">
    <div id="c"><img src="..."></div>
</div>

JS

var attribute;

function myFunction() {
    attribute = this.getAttribute('data-card');
}

var symboxes = document.getElementsByClassName('sym-box');

for (i = 0; i < symboxes.length; i++) {
  symboxes[i].addEventListener('click', myFunction, false);
}

The code above works as expected, 'this' references the clicked element. However, when I write it as an arrow function I get "Uncaught TypeError: this.getAttribute is not a function" because 'this' is now referring to the window object.

const myFunction = () => {
    attribute = this.getAttribute('data-card');
};

So my question is how do I rewrite myFunction() as an arrow function where 'this' refers to the clicked element?

NOTE: My question has been marked as a duplicate but I am asking for a work around to achieve the same thing when using an arrow function. Just being told "You can't" or "Don't" didn't answer my question.

Coffee bean
  • 1,474
  • 15
  • 27
  • 1
    simple, you can't - arrow functions `this` is very clearly defined, and it can not be the clicked element – Jaromanda X Dec 01 '17 at 10:23
  • 1
    If for some reason you insist on using arrow syntax, use `event.currentTarget` instead of `this`. – Bergi Dec 01 '17 at 11:12

2 Answers2

9

So my question is how do I rewrite myFunction() as an arrow function where 'this' refers to the clicked element?

With myFunction defined as arrow-function, this refers to the scope in which it is defined.

Use event.target (in case you still want to use arrow function)

const myFunction = (event) => {
    attribute = event.currentTarget.getAttribute('data-card');
};
gurvinder372
  • 66,980
  • 10
  • 72
  • 94
  • @Coffeebean If you did not use arrow function but a regular function `var myFunction = function()... ` then you could do it with `fun.bind` (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind) - . `symboxes[i].addEventListener('click', myFunction.bind(symboxes[i])`. Fiddle: https://jsfiddle.net/0o1ujo4c/. But the answer posted solves the problem in a decent way. – tiblu Dec 01 '17 at 10:46
  • 1
    You will want to use `.currentTarget`, not `.target`. Most likely the image will be clicked, and it doesn't have a `data-card` attribute. – Bergi Dec 01 '17 at 11:14
  • @Bergi Yes you are right, the img is returned – Coffee bean Dec 01 '17 at 11:15
  • @bergi made the change, thanks for highlighting the same. – gurvinder372 Dec 01 '17 at 11:17
2

You cannot override the this value of an arrow function. The this value for that arrow function would be taken from its execution context. Once it is bound, it cannot be replaced or overridden. This is the basic rule.

const myFunction = () => {
    attribute = this.getAttribute('data-card');
};

So here in your code, the this value available at the scope of myFunction variable declaration will be taken and bind to the function. The rule of thumb here is, DON'T use arrow function for event listeners.

You can read more about arrow functions here.

Rajaprabhu Aravindasamy
  • 66,513
  • 17
  • 101
  • 130
  • 1
    "The rule of thumb here is, DON'T use arrow function for event listeners." So how do you go about it when you need, in an even listener, to access both a class instance (where the listener is defined) and the target? Using an arrow function + `event.currentTarget` sounds like the right approach then. – Fabien Snauwaert Jun 04 '22 at 03:12