javascriptscopepass-by-referencepass-by-value

How can I pass the current value of a variable to an event handler


Let's say I have this group of 'child' elements within a parent element called iBlock, and within a function I'm adding an 'onclick' event to each child, like so...

someFunction(iBlock) {
    var el, iTotal = iBlock.children.length;
     for (var i= 0; i < iTotal; i++) {
             el=iBlock.children[i];
             el.onclick=function(event){myHandler(this, i)};
             }
    }

So given an onclick handler like this...

function myHandler(elem, n) {
     alert("number is: " +  n);
    }

It was a little surprising that regardless of which child element I clicked on, the handler in this example would always display the one less than the total number of children. But I understand that the total - 1 was highest number the variable 'i' got to before the loop ended, and the handler isn't going to pass anything but the value of the variable at the time the event occurs. But understanding that doesn't help me answer these 2 questions...

  1. If I really wanted the second argument in the onclick function to pass the value 'i' actually was at the time the onclick handler was attached, what could I pass instead of the raw 'i' variable?
  2. Since 'i' was declared by a 'var' keyword within the function that added the handlers, why did it still even have a value (in this case the total - 1), after installing function had returned? I would have been less surprised if the handler displayed 'undefined'. I realize this is a 'scope of variables' question, but I'm sure its related.

On the first question, I've tried passing: i+'x', just to turn it into a string, hoping it would then generate a unique instance. That doesn't work either... and element 'clicked' on just triggers the handler shown to display '4x'. I've also tried creating a second variable, as a string, then extracting the numerical portion, and passing the final 'value' from the new variable to the onclick handler, like this...

someFunction(iBlock) {
        var el, iTotal = iBlock.children.length;
         for (var i= 0; i < iTotal; i++) {
            el=iBlock.children[i];
            var n= i+'x';  // make 'i' into a new longer string variable
            var r = /\d+/;    // regex, to capturei digits
            n = n.match(r); // convert the string to only contain the digits
            el.onclick=function(event){myHandler(this, n)};
            }
        }

No change. the handler still displays the total-1 on any element I click.

I have looked at similar posts on this subject such as How can I extract a number from a string in JavaScript?, and I still don't understand a remedy to my first question.

PS (I've not tried creating the variable with 'let' instead of 'var', because I'm pretty committed to supporting certain older browsers). EDIT: I'd like to include IE-8 among those 'old browsers' if possible. I realize I can't always do so, but in this case I'd like to.

EDIT: I have found an interesting workaround, which is to add my own variable to each element, in the loop where I add handlers, like this...

someFunction(iBlock) {
    var el, iTotal = iBlock.children.length;
     for (var i= 0; i < iTotal; i++) {
             el=iBlock.children[i];
             el.onclick=function(event){myHandler(this, i)};
             el.myIndex=i;
             }
    }

Then of course in my handler, where I'm passing 'this' anyway, I can properly recover the desired value of 'i' by examining 'this.myIndex. I've heard this is dangerous, because it might clash with a future DOM property.

But that seems 'kludge', so I'm still interested in the answers!


Solution

  • After careful consideration of all the answers offered, I'm going to stick with the solution I discovered and posted, in the final edit of my OP. Specifically, It is assigning a new attribute to each element as I'm adding my events...

    someFunction(iBlock) {
        var el, iTotal = iBlock.children.length;
         for (var i= 0; i < iTotal; i++) {
                 el=iBlock.children[i];
                 el.onclick=function(event){myHandler(this)};
                 el.myIndex=i;
                 }
        }
    

    Here, I'm no longer even attempting to send the value of 'i' to the handler as a second arg, due the to problems noted. Yet for whatever reason, when the added element 'myIndex' is assigned to 'i', it retains the identifying value at the time of assignment. So when the handler is called it can be accessed via 'this.myIndex'. I don't fully understand why doing it this way causes 'i' to be passed by value rather than a reference / instance of itself, but it works.

    No one has offered any reason NOT to do this, and it seems the least complex in terms of added code. I believe the only risk is choosing a name that clashes with some future 'standard' attribute. Maybe this is why I see suggestions tp call add on sttributes something like "data-myindex". But either way, it seems to work all IE platforms I've tried back to IE8, also Edge, Chrome and Firefox back to the last ones distributed for Windows-XP through the latest on Win-10. It works on Mobil Safari back to IOS-7, and all the android devices I'm able to test with. Still open to suggestions, but sometimes, as they say, "the simplest solution is the best"