0

This code will pass the last value created by the loop the eventListener function, I need the value at the time the eventListener was created to be attached.

window.onload = function() {

var el = document.getElementsByClassName('modify');

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

     var getId=el[i].id.split("_");

     document.getElementById("modify_y_"+getId[2]).addEventListener('mouseover', function() {
     document.getElementById("modify_x_"+getId[2]).style.borderBottom = '#e6665 solid 3px';
    }, false); 

}
}
Guesser
  • 1,769
  • 3
  • 25
  • 52
  • 1
    Very common question, here is one explanation: http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – Matt Greer Aug 31 '13 at 18:28
  • @MattGreer: using closures has been best practise for years, but imo we should start teaching people to use `bind` instead since it is a superior solution and will soon be built in to all browsers that people are targeting – Martin Jespersen Aug 31 '13 at 18:55
  • @MartinJespersen cool, I didn't realize you can pass args along to bind. I still haven't used it much as we support old IEs. Thanks for the heads up. – Matt Greer Aug 31 '13 at 19:07

2 Answers2

1

You do that by using a builder function:

window.onload = function () {

    var el = document.getElementsByClassName('modify');

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

        var getId = el[i].id.split("_");

        document.getElementById("modify_y_" + getId[2]).addEventListener('mouseover', makeHandler(getId[2]), false);

    }

    function makeHandler(theId) {
        return function () {
            document.getElementById("modify_x_" + theId).style.borderBottom = '#e6665 solid 3px';
        };
    }
};

The function returned by makeHandler closes over the theId argument, which doesn't change.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

You can use the bind prototype which exists on all functions in modern browsers

window.onload = function() {

var el = document.getElementsByClassName('modify');

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

     var getId=el[i].id.split("_");

     document.getElementById("modify_y_"+getId[2]).addEventListener('mouseover', function(theid) {
       document.getElementById("modify_x_"+getId[2]).style.borderBottom = '#e6665 solid 3px';
    }.bind(null,getId[2]), false); 

}
}

If you need to support older browsers that doesn't have bind nativly built in, you can use this poly-fill taken from MDN where you will also find documentation on the bind prototype function

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
Martin Jespersen
  • 25,743
  • 8
  • 56
  • 68
  • What is the first parameter null for btw? – Guesser Aug 31 '13 at 19:51
  • The first paramater sets the `this`value of the function - since you don't need a specific `this` I set it to null - read the supplied documentation link to fully understand the function – Martin Jespersen Aug 31 '13 at 19:54