I am having a bit of trouble with a function I am trying to debug.
The function is supposed to load a bunch of mouseover events for various elements in the page and display information about a province/region in a div when that province/region is moused over.
Here is the code I am using, (I know that this code is poorly written, by logically I don't see why it doesn't work):
Code:
function provInfoBox()
{
$(document).ready(function()
{
for(var i = 2; i <= 11; i++)
{
var data = provData[i].split(",");
var provName = data[0];
var provRate = data[dateVal];
var str = "Rural " + provName + ": " + provRate + '%';
switch(i)
{
case 2:
{
for(var j = 1; j <= 5; j++)
{
document.getElementById('Newfoundland and Labrador ' + j).addEventListener('mouseover', function() {return ieTip(str)}, false );
document.getElementById('Newfoundland and Labrador ' + j).addEventListener('mouseout', clearTipIE, false );
}
break;
}
...
case 11:
{
for(var j = 1; j <= 6; j++)
{
document.getElementById('British Columbia ' + j).addEventListener('mouseover', function() {return ieTip(str)}, false);
document.getElementById('British Columbia ' + j).addEventListener('mouseout', clearTipIE, false);
}
break;
}
default:
{
alert("Error.");
}
}//end switch
}//end root foor loop.
});//end document ready statement
}//end provInfoBox()
The problem that is occuring, is when a mouse event is fired off it uses the last value for "str", which in the case of this script is always going to contain the data for "British Columbia", as it is the last province defined in the switch statement.
I would have assumed that whatever the present value of str was at the time the event listener was added would get sent, rather then the present value of str when the method is called.
Is there an alternative way to do this?
*I know that using spaces in element ID's is not wise, unfortunately I am not able to change this and I know that this is not the cause of my problem*
I think this has to do with the variable being passed to the function ieTip() by it's reference rather then it's value.
However I have tried using str.value instead of str, but it still doesn't work, it just gives me the contents of str.value as "undefined", even if I have tried defining it by "str.value = ..."
You can use a closure to have different values for str for each call. That way, new variables are created for each iteration in the loop, rather than reusing the old ones.
PHP Code:
function provInfoBox() { $(document).ready(function() { for(var i = 2; i <= 11; i++) (function() { //[START CLOSURE] var data = provData[i].split(","); var provName = data[0]; var provRate = data[dateVal]; var str = "Rural " + provName + ": " + provRate + '%';
var str = '';
for(var i = 0; i < 10; i++)
(function() { // begin closure
var enclosedStr;
str += i + ',';
enclosedStr = str;
setTimeout(function() {
document.body.innerHTML += enclosedStr + '\<br /\>';
}, 100);
})(); // end closure
};
</script>
That's because this way a function is called each time the for loop executes, and the function contains a definition for "enclosedStr". That means that variable, rather than being created once (like str), is created 10 times. Therefore, every time the timeout executes, it retrieves its corresponding enclosedStr.
This technique is called closure, and it is very helpful in JavaScript.
I am reading up on closures now, I tried to run the code you provided and it doesn't quite work, I am getting an error in firebug "Function requires a name"
*NVM* I figured it out, was just missing an opening brace on the for loop.
Thanks .
Last edited by DaveRich; 04-19-2012 at 01:15 PM.
Reason: Figured it out
I thought I had the closure thing figured out, however when I tried to implement it in another section of my page I have the same sort of error.
The output of all of these mouseover functions for the various cities is:
"undefined: undefined%". This sounds like a closure problem as the arrays containing the data are not defined outside the scope of this function.
HTML Code:
function cityTextInfo()
{
$(document).ready(function()
{
var cityNames = ["City","St. John's","Halifax","Moncton","Saint John","Saguenay","Québec","Sherbrooke","Trois-Rivières",
"Montréal","Gatineau","Ottawa","Kingston","Peterborough","Oshawa","Toronto","Hamilton","St. Catherines-Niagara",
"Kitchener-Cambridge-Waterloo","Brantford","Guelph","London","Windsor","Barrie","Greater Sudbury","Thunder Bay",
"Winnipeg","Regina","Saskatoon","Calgary","Edmonton","Kelowna","Abbotsford-Mission","Vancouver","Victoria"];
var cityRateData = new Array();
for(var i = 1; i <= 34; i++)
{
var curData = cityData[i].split(",");
cityRateData[i] = curData[dateVal];
}
for(var i = 1; i <= 34; i++)
{
(function() //Start of closure
{
document.getElementById(cityNames[i]).addEventListener('mouseover', function() {return ieTip(cityNames[i] + ": " + cityRateData[i] + '%')}, false);
document.getElementById(cityNames[i]).addEventListener('mouseout', clearTipIE, false);
})(); //End of closure
}
});
}//end cityTextInfo()
The problem here is there are no new variables defined inside the closure.. All are still external to the closure. Try this:
PHP Code:
for(var i = 1; i <= 34; i++) { (function() //Start of closure { // define some variables within the scope of the closure var cityName = cityNames[i]; var cityRateDatum = cityRateData[i];
Bookmarks