Javascript multiple dynamic addEventListener creat

2019-01-07 23:10发布

I want to use event listeners to prevent event bubbling on a div inside a div with onclick functions. This works, passing parameters how I intended:

<div onclick="doMouseClick(0, 'Dog', 'Cat');" id="button_id_0"></div>
<div onclick="doMouseClick(1, 'Dog', 'Cat');" id="button_id_1"></div>
<div onclick="doMouseClick(2, 'Dog', 'Cat');" id="button_id_2"></div>

<script>
function doMouseClick(peram1, peram2, peram3){
    alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}
</script>

However, I tried to create multiple event listeners in a loop with this:

<div id="button_id_0"></div>
<div id="button_id_1"></div>
<div id="button_id_2"></div>

<script>
function doMouseClick(peram1, peram2, peram3){
    alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

    for (var i=0; i<names.length; i++){

        document.getElementById(names[i]).addEventListener("click", function(){
        doMouseClick(i, "Dog", "Cat");

    },false);

}

</script>

It correctly assigns the click function to each div, but the first parameter for each, peram1, is 3. I was expecting 3 different event handlers all passing different values of i for peram1.

Why is this happening? Are the event handlers not all separate?

4条回答
Summer. ? 凉城
2楼-- · 2019-01-07 23:40

Problem is closures, since JS doesn't have block scope (only function scope) i is not what you think because the event function creates another scope so by the time you use i it's already the latest value from the for loop. You need to keep the value of i.

Using an IIFE:

for (var i=0; i<names.length; i++) {
  (function(i) {
    // use i here
  }(i));
}

Using forEach:

names.forEach(function( v,i ) {
  // i can be used anywhere in this scope
});
查看更多
够拽才男人
3楼-- · 2019-01-07 23:41

As pointed out already the problem is to do with closures and variable scope. One way to make sure the right value gets passed is to write another function that returns the desired function, holding the variables within the right scope. jsfiddle

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

function getClickFunction(a, b, c) {
  return function () {
    doMouseClick(a, b, c)
  }
}
for (var i = 0; i < names.length; i++) {
  document.getElementById(names[i]).addEventListener("click", getClickFunction(i, "Dog", "Cat"), false);
}

And to illustrate one way you could do this with an object instead:

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

function Button(id, number) {
  var self = this;
  this.number = number;
  this.element = document.getElementById(id);
  this.click = function() {
    alert('My number is ' + self.number);
  }
  this.element.addEventListener('click', this.click, false);
}
for (var i = 0; i < names.length; i++) {
  new Button(names[i], i);
}

or slightly differently:

function Button(id, number) {
  var element = document.getElementById(id);
  function click() {
    alert('My number is ' + number);
  }
  element.addEventListener('click', click, false);
}
for (var i = 0; i < names.length; i++) {
  new Button(names[i], i);
}
查看更多
淡お忘
4楼-- · 2019-01-07 23:43

Everything is global in javascript. It is calling the variable i which is set to 3 after your loop...if you set i to 1000 after the loop, then you would see each method call produce 1000 for i.

If you want to maintain state, then you should use objects. Have the object have a callback method that you assign to the click method.

You mentioned doing this for event bubbling...for stopping event bublling, you really do not need that, as it is built into the language. If you do want to prevent event bubbling, then you should use the stopPropagation() method of the event object passed to the callback.

function doStuff(event) {
    //Do things
    //stop bubbling
    event.stopPropagation();
}
查看更多
何必那么认真
5楼-- · 2019-01-07 23:47

It's because of closures.

Check this out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

The sample code and your code is essentially the same, it's a common mistake for those don't know "closure".

To put it simple, when your create a handler function, it does not just accesses the variable i from the outer environment, but it also "remembers" i.

So when the handler is called, it will use the i but the variable i is now, after the for-loop, 2.

查看更多
登录 后发表回答