1

I am generating an HTML table dynamically using JavaScript. One of the table's columns contains an onmouseover event, and when the mouse cursor is over a particular cell, I wish to display cell specific information.

In my case, I wish to display the value of nPos via an alert() call as it was during the table construction.

Simplified example of my table creation:

for (var iLoop = 0 ; iLoop < nHitsPerPage ; iLoop++) {
    var nPos = (nCurrentPageParam * SearchResults.nMaximumHitsPage) + iLoop;

    //...

    var cellInfo = tableResultsRow.insertCell(6);

    //...

    cellInfo.className = "noWrapCell";
    cellInfo.innerHTML = "?";
    cellInfo.style.cursor = "pointer";

    // The line below is important - I need to store nPos value during the table consturction
    cellInfo.onmouseover = function(){alert("nPos = " + nPos)};
    cellInfo.onmousemove = function(){FileResort.LightboxShortInfo.update(event)};
    cellInfo.onmouseout = function(){FileResort.LightboxShortInfo.hide(event)};
}

As you can see from the example above, I iteratively created a table within my for() loop. I want to store the value of the nPos within every row (record) which is different for each row (record).

My problem is that once I mouseover that particular cell, I get the same nPos value for every row (record), and that nPos is the current value of nPos at the particular application state.

I cannot find a way to record the nPos value as it was during the for() execution, which is important for me in identifying what record is stored in the particular row.

Is there a way to capture, or store the value of nPos for every row (record) during the table's initial construction?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Bunkai.Satori
  • 4,698
  • 13
  • 49
  • 77

2 Answers2

2

You are yet another victim of the closure monster :)

Look at this:

cellInfo.onmouseover = function(){alert("nPos = " + nPos)};

the function here - let's call it lambda - will have to find the value of the variable nPos. Since nPos is not declared inside lambda, it will look for it in the upper level function, that is the function where nPos is created.

When the mouseover event will fire, the code that declares and sets nPos will already have run to completion, so nPos will have the value nHitsPerPage.

That is exactly what lambda will display. Unfortunately, that's not what you want :).


To get over that, you need to create a (lexical) closure, i.e. provide lambda with a value of nPos that suits your needs.

The way of doing it in Javascript is as follows:

cellInfo.onmouseover = function(p){
    return function () { alert("nPos = " + p)};
    } (nPos);

let's call nu the new function (the one that takes p as a parameter). We changed lambda so that it now refers to p instead of nPos.

It means that when the mouseover event will fire, lambda will look for p in its upper function, which is now nu. And it will indeed find p there, as the parameter of nu.

p is a function parameter, so it recieves a copy of nPos at the time it is called. It means lambda will refer to a different instance of nu's calling context for each instance of cellInfo.

Each instance of the mouseover event handler will now hold a copy of p that is set to the desired value of nPos.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43
  • what a brilliant answer. Thank you very much! I have already tested it and it does work perfectly. Despite I understand all your explanation, the whole construction looks to me a bit complicated. Would you be so kind and explain it a bit more? Particulary, the need to have so many functions. Why can not I simply use `cellInfo.onmouseover = alert("my output");` Secondly, I do not get the need of `return function ()` in your example. Thank you very much for great explanation in your answer above, anyway. – Bunkai.Satori Jan 04 '14 at 23:33
  • 1
    Thanks. You may want to have a look at [this answer to a similar problem](http://stackoverflow.com/questions/20864580/how-to-pass-variable-through-a-functions-function/20864763#20864763). Closures are a bit strange in JavaScript because, contrary to most other languages, the visibility of variables is not limited to the code block they are defined in, but "hoisted" to the the start of the function they are declared in. This has all kinds of unusual consequences, one of them being that you need to declare a function just to get a new scope for a variable (the `nu` function in your case). – kuroi neko Jan 04 '14 at 23:43
  • Thank you again. I see, you have spent significant effort explaining similar question in the other place. I will study it in detail. Let me give you +1 for every your answer/comment and mark your answer as the *accepted* one. Thanks. – Bunkai.Satori Jan 04 '14 at 23:48
  • Thanks again, and welcome to the fascinating world of the JavaScript engine :). – kuroi neko Jan 04 '14 at 23:51
0

Quick Psuedo Code:

var nPosArray = [];
var itemsArray = [];

for (var iLoop = 0 ; iLoop < nHitsPerPage ; iLoop++)
{
    var nPos = (nCurrentPageParam * SearchResults.nMaximumHitsPage) + iLoop;

    //...

    var cellInfo = tableResultsRow.insertCell(6);

    //...

    cellInfo.className = "noWrapCell";
    cellInfo.innerHTML = "?";
    cellInfo.style.cursor = "pointer";

    // The line below is important - I need to store nPos value during the table consturction
    nPosArray.push(nPos);
    cellInfo.addEventListener('mouseout', cellMouseOut(event));

    cellInfo.onmousemove = function(){FileResort.LightboxShortInfo.update(event)};
    cellInfo.onmouseout = function(){FileResort.LightboxShortInfo.hide(event)};
    itemsArray.push(cellInfo);
}

function cellMouseOut(e)
{
    for(iLoop = 0 ; iLoop < cellInfo.length; iLoop++)
    {
        if(e.target.id == cellInfo[iLoop].id)
        {
            alert('NPos: ' + nPosArray[iLoop]);
        }
    }
}
Cilan
  • 13,101
  • 3
  • 34
  • 51
  • Mmmm... Is there a law against creating functions inside a loop? Compared with what your usual JQuery application does before user code even starts to execute, setting up a handful of event handlers seems relatively harmless to me. Or is there something I'm missing? – kuroi neko Jan 04 '14 at 23:10
  • @kuroineko Integrated Developers Environment – Cilan Jan 04 '14 at 23:18
  • 1
    LOL since my mother left my dad for an IDE, I am unable to trust them. Sneaky bastards, these IDEs, the whole lot of them. – kuroi neko Jan 04 '14 at 23:21