Reputation: 4758
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?
Upvotes: 1
Views: 2765
Reputation: 8661
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
.
Upvotes: 2
Reputation: 13809
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]);
}
}
}
Upvotes: 0