2

I need to add some listeners to 8 object (palms). These object are identical but the behaviour have to change basing to their position. I have the follow (ugly) code:

root.palmsStatus = ["B","B","B","B","B","B","B","B"];

if (root.palmsStatus[0] !== "N")
  root.game.palms.palm1.addEventListener("click", palmHandler = function(){ palmShakeHandler(1); });
if (root.palmsStatus[1] !== "N")
  root.game.palms.palm2.addEventListener("click", palmHandler = function(){ palmShakeHandler(2); });
if (root.palmsStatus[2] !== "N")
  root.game.palms.palm3.addEventListener("click", function(){ palmShakeHandler(3); });
if (root.palmsStatus[3] !== "N")
  root.game.palms.palm4.addEventListener("click", function(){ palmShakeHandler(4); });
if (root.palmsStatus[4] !== "N")
  root.game.palms.palm5.addEventListener("click", function(){ palmShakeHandler(5); });
if (root.palmsStatus[5] !== "N")
  root.game.palms.palm6.addEventListener("click", function(){ palmShakeHandler(6); });
if (root.palmsStatus[6] !== "N")
  root.game.palms.palm7.addEventListener("click", function(){ palmShakeHandler(7); });
if (root.palmsStatus[7] !== "N")
  root.game.palms.palm8.addEventListener("click", function(){ palmShakeHandler(8); });

I have two needs:

1) doesn't use an anonymous function on click event.

I wrote this code, but it doesn't work

root.game.palms.palm8.addEventListener("click", palmShakeHandler(8));

So this one works fine

root.game.palms.palm8.addEventListener("click", function(){ palmShakeHandler(8); });

But I didn't understand how remove the event listener. I try this solution, but it doesn't work

root.game.palms.palm8.addEventListener("click", palmHandler = function(){ palmShakeHandler(8); });
root.game.palms.palm8.removeEventListener("click", palmHandler);

2) add and remove listener in a for cycle

I wrote the follow code but the behaviour is not correct.

  for (i=1; i <= root.palmsStatus.length; i++){
    if (root.palmsStatus[i-1] !== "N"){
      root.game.palms["palm" + i].addEventListener("click", function(){ palmShakeHandler(i); });
    }
  } 

the listeners was added but the value of the parameter passed to the palmShakeHandler is always 8.

Nobody could help me to fix these issues?

Zauker
  • 2,344
  • 3
  • 27
  • 36

3 Answers3

4

There is a actually, a perfect way to do that in JavaScript using the Function.prototype.bind method.

bind let you define extra parameters that will be passed, as arguments, of the function.

You should also keep in mind that bind creates a new function and doesn't modify the initial function.

Here is what it looks like:

function palmHandler(number) {
  // your code working with `number`
}

var palmHandler8 = palmHandler.bind(null, 8)
// the palmHandler8 is now tied to the value 8.
// the first argument (here null) define what `this` is bound to in this function

This should fix your problem, and you will be able to remove handlers easily :)

Your code will look like this:

for (i=1; i <= root.palmsStatus.length; i++){
  if (root.palmsStatus[i-1] !== "N"){
    root.game.palms["palm" + i].addEventListener("click", palmShakeHandler.bind(null, i));
  }
} 

To be able to remove the handler afterward, you need to keep a reference to the function you create with bind. This would be the way to do this.

var boundHandler = handler.bind(null, i);
element.addEventListener(boundHandler);
element.removeEventListener(bounderHander);

If you want to know more about the awesome bind method in JavaScript, the MDN is your friend :) https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind

BTW, the problem with you function always returning 8 is a very common question in JavaScript. This thread will explain everything (spoiler, it's a matter of scoping :) ) https://stackoverflow.com/a/750506/2745879

atomrc
  • 2,543
  • 1
  • 16
  • 20
  • Adding listener into the cycle woks fine. But removing them doesn't work – Zauker May 30 '17 at 09:05
  • I use this code: root.game.palms["palm"+palm].removeEventListener("click", palmShakeHandler); – Zauker May 30 '17 at 09:06
  • Oh ok, to remove the handler, you need to keep a reference to the bound function and remove this one. `var boundPalm = palm.bind(null,i); addHandler(boundPalm) ... removeHandler(boundPalm)` – atomrc May 30 '17 at 09:08
  • Awesome :) Welcome in the fabulous world of `bind` :D – atomrc May 30 '17 at 09:28
  • If you are using EaselJS, you can also use the shortcut `on()`, which does binding for you, and takes a 3rd parameter (scope). http://www.createjs.com/docs/easeljs/classes/EventDispatcher.html#method_on – Lanny May 30 '17 at 15:12
0

In your last solution you are pasing the same var to every function and that is what make al the functions work with 8 because is the last value of the variable.

To work arround that you can use "let" ( please at least use var, otherside that "i" is global and can be changed every where in the code) but since I dont know wich browser you target I propose other solution.

for (var i=1; i <= root.palmsStatus.length; i++){
    if (root.palmsStatus[i-1] !== "N"){
      root.game.palms["palm" + i].addEventListener("click", (function(index)
      (return function(){ 
        palmShakeHandler(index); 
      }))(i);
    }
  } 

Since its look like You are targeting modern browsers I will use let.https://kangax.github.io/compat-table/es6/

for (var i=1; i <= root.palmsStatus.length; i++){
    let index = i;
    let intermediateFunction = function(){palmShakeHandler(index);};
    if (root.palmsStatus[i-1] !== "N"){
      root.game.palms["palm" + i].addEventListener("click",intermediateFunction);
      root.game.palms["palm" + i].removeHandShake = function(){this.removeEventListener("click",intermediateFunction)};
    }
  } 

So now you just need to call "removeHandShake" and will remove the listener,

I have code this right here so it ease some minor errors to pop

  • This seems a good solution, but I don't understand how remove the listeners added. – Zauker May 30 '17 at 08:55
  • well since you neeed the function reference to do the removeEventListener you need to store it somewhere, I propose using a closure. Let me update the top answer – Raising Spirit May 30 '17 at 09:29
0

So in case your array of »palms« is very huge, it is basically a bad Idea to add a single event listener to each of them, because that causes performance flaws. So I would suggest a different approach:

var handlers = [function (e) {}, …, function (e) {}];

root.game.palms.forEach(functiion (palm, idx) {
  palm.setAttribute('data-idx', idx);
});

<palmsparent>.addEventListener('click', function (e) {
  var c = e.target, idx = -1;

  while (c) {
    if (c.hasAttribute && c.hasAttribute('data-idx')) {
      idx = parseInt(c.getAttribute('data-idx'));
      break;
    }
    c = c.parentNode;
  }

  //here you also check for the »palm status«
  if (idx >= 0) {
    handlers[idx](c);
  }
})

One event listener for all, much easier to remove and better for performance.

philipp
  • 15,947
  • 15
  • 61
  • 106