2

I'm still new to JavaScript and i'm having the below issue.

I'd like to name/declare my function and then use its name as listener to add/remove click events easily. the main reason for this is so I can remove the click events whenever some condition happens.

Brief of what i'm trying to achieve:

function game() {
// 
}

The problem i'm having is when I add the event like this:

for (let i = 0; i < cards.length; i++) {
    card = cards[i];
    card.addEventListener('click',  game);
}

I get an error with the named function game that says:

i is not defined

However, this error doesn't happen when I put the function as anonymous inside the listener parameter.

for (let i = 0; i < cards.length; i++) {
    card = cards[i];
    card.addEventListener('click', function game() {
      //
}

Declaring i globally didn't work nor passing i as parameter.

  • Full code with anonymous function: Here
  • Full code with named function (what I want to work): Here
Abdulelah
  • 77
  • 3
  • 10
  • Possible duplicate of [Pass an extra argument to a callback function](https://stackoverflow.com/questions/40802071/pass-an-extra-argument-to-a-callback-function) – str Feb 03 '19 at 10:22

3 Answers3

1

To access different is, you need different closures, and therefore you got different function references. You could store them directly in the card to be able to remove them later on:

for (let i = 0; i < cards.length; i++) {
  const card = cards[i];
  card.addEventListener('click', card._click = function game() {
    //
  });
}

Then you can remove it later with:

card.removeEventListener("click", card._click);

The same also works if you move game outside and add a curried parameter for i (might be cleaner):

const game = i => () => {
  // ...
 };

for (let i = 0; i < cards.length; i++) {
  const card = cards[i];
  card.addEventListener('click', card._click = game(i));
}
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Thanks a lot for the help! One question if you don't mind, why can't I rename "card._click" to something like "cardListener" for example? – Abdulelah Feb 04 '19 at 06:57
  • @abdulelah you can :) just make sure that the property doesnt exist on card already... – Jonas Wilms Feb 04 '19 at 12:49
0

Your issue is that your game function doesn't know about i because i doesn't exist within your game function due to variable scoping. In order to fix this you can pass i as a argument into game like so:

card.addEventListener('click', _ => game(i));

In the above line, your calling an arrow function, which then calls the game function. This allows you to pass an argument into game.

And then in your game method accept the variable i as an argument:

function game(i) {

See working example here

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
0

You should read about lexical scoping in javascript to understand why i is not available inside the named function game

Inside game function, it can only refer variables that are within in lexical scope (or inside this which is a topic of its own)

Simply put, you can pass the 'i' to the game to get it working. But since the addEventListener expects a function, you should create a function factory that employs a bit of closure

edit your game as

let game = (i) => () => {
  //existing game function 
}

now use card.addEventListener('click', game(i)); and it should work as expected

Here is a working fiddle for your reference : https://jsfiddle.net/uch1arny/2/

Dhananjai Pai
  • 5,914
  • 1
  • 10
  • 25