...

View Full Version : Advanced JS question - object scope problem



tdellaringa
04-18-2005, 04:40 PM
I'm working on a web application, and they have requested a dynamic help system. They want 'tool
tip' type help. I have taken the code from Nicer titles at
(http://neo.dzygn.com/archive/2003/12/nicer-titles) as a basis for what I am doing. That code is
great but I need to drop more data into the 'tool tip' than what a title attribute would allow -
essentially a paragraph of text.

I've set this up as an object. The problem I am having is that it seems some variables appear to
be out of scope, and I'm not sure where I am making my mistake.

Quick rundown on the code:

Set up the class:

function DynamicHelp(nDelay, sContainerID, sClassName)
{
var oTimer;
var isActive = false;
var nDelay;
var sNameSpaceURI = "http://www.w3.org/1999/xhtml";

if(!nDelay || nDelay <= 0){ nDelay = false;}
if(!sContainerID){ sContainerID = "helpdiv";}
if(!sClassName){ sClassName = "helpbox";}

// write help div to page
var oContainer = document.getElementById(sContainerID);
if(!oContainer)
{
oContainer = document.createElementNS ? document.createElementNS(sNameSpaceURI, "div") :
document.createElement("div");
oContainer.setAttribute("id", sContainerID);
oContainer.className = sClassName;
oContainer.style.display = "none";
document.getElementsByTagName("body").item(0).appendChild(oContainer);
}

//=====================================================================
// Method addElements (Public)
//=====================================================================
this.addElements = function addElements()
{
// find all elements that have a span attached
var spanArray = document.getElementsByTagName("span");
for(var i=0;i<spanArray.length;i++)
{
// but grab only the 'help' spans
if(spanArray[i].className == "help")
{
// attach events
addEvent(spanArray[i], 'mouseover', show);
addEvent(spanArray[i], 'mouseout', hide);
addEvent(spanArray[i], 'focus', show);
addEvent(spanArray[i], 'blur', hide);
}
}
}
}

The addElements() method is where I grab every span that has a class of 'help' and add the events.
Up to this point, everything is working fine. Those methods are called on mouseover etc:

function addEvent(obj, evType, fn){
if(obj.addEventListener){
obj.addEventListener(evType, fn, false);
return true;
} else if (obj.attachEvent){
var r = obj.attachEvent('on'+evType, fn);
return r;
} else {
return false;
}
}

function show(){
if(nDelay){
oTimer = setTimeout(function(){oContainer.style.display = "block";}, nDelay);
} else {
oContainer.style.display = "block";
}
}

function hide(){
oContainer.style.display = "none";
clearTimeout(oTimer);
}

My error is showing up in the show() method. the vars nDelay and oContainer seem to be out of
scope :

"Error: oContainer is not defined" - of course it is defined in the class, but this method is not
seeing that. Below is how I am writing it out to the page:

DynamicHelp.autoCreation = function()
{
if(!document.getElementsByTagName){ return; } //not supported
DynamicHelp.autoCreated = new Object();
DynamicHelp.autoCreated.help = new DynamicHelp(600);
DynamicHelp.autoCreated.help.addElements();
}

addEvent(window, "load", DynamicHelp.autoCreation);


You can see the complete page (all code inline) at:

http://www.pixelmech.com/rev/help2.html

TIA

Tom

brothercake
04-18-2005, 07:10 PM
timeouts are in the scope of the window, not your object. You need to create a variable that's private to the method, and that will be global within (and unique to) the scope of that method, so then you can reference that from inside the timeout.

It actually sound more complicated than it is:


var thisContainer = oContainer;
oTimer = setTimeout(function(){thisContainer.style.display = "block";}, nDelay);

Which is the same trick as going "var self = this" before an expando handler, so that you have a reference to the calling object from inside it.

btw - your "nDelay" variable is confusing - is it a number or a boolean? It seems to be used as both at different times.

tdellaringa
04-18-2005, 07:19 PM
Thanks BC - I should have put my show/hide methods inside the class. I've done that and now it works well. The nDelay is a number, although it checks for it as a boolean to see if it has been set, I see where you say that is confusing. I should probably do:

if(nDelay > 0){
oTimer = setTimeout(function(){oContainer.style.display = "block";}, nDelay);
}
else {
oContainer.style.display = "block";
}

And set it to 0 if it is not passed as an argument when constructed. Would that make more sense? Most of this code is nicked from niceTitles and I've only changed what I needed for it to work.

brothercake
04-18-2005, 07:39 PM
Keeping it as a number would be the thing to do - if you're comparing it as a boolean when the value might be zero, you're relying on automatic datatype conversion, and that's not a good thing to do, espcially if you're using the letter-prefix convention to indicate datatype.

tdellaringa
04-18-2005, 08:21 PM
Yep, switched it and it works just fine. Thanks!



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum