Forum Moderators: open

Message Too Old, No Replies

fighting with setInterval

setInterval appears to modify its scope in a surprising manner

         

r3dlp

9:47 pm on Dec 30, 2008 (gmt 0)

10+ Year Member



Hello,

This is my first post here, so I ask in advanced that you please be gentle and excuse my ignorance if I inadvertently trample on any of the posting etiquette policies (both written and unwritten), and please let me know if/when I do screw up, so that I'll know to not do that again.

I thank you in advanced for your graciousness, and now I'll proceed with my issue.

The subject basically say it all: I'm fighting with setInterval. In this post, I'm including 2 iterations of the same code: 1) a bare-bones version (the working code), and 2) a version closer to what I'd like to use in production (although it's still pretty far from production-grade code IMHO).

My goal was (and still is) to partition the code into 3 parts: 1) the initialization code (initHex(...) and fade(...)), 2) the display/canvas handling code (setBgColor(...) and doIt(...) in this case), and 3) the timer code. The reason for the increasing complexity is that I ultimately intend to set up a graphics pipeline for rendering various objects.

The problem, that I'd like to resolve, feels like I'm dealing with some kind of scope issue. The setInterval(...) call in the first code fragment works correctly, but the call in the second code fragment appears to go into while(1) (or do-forever) loop. (I've tested this with several versions of Firefox [most recently with version 3.0.5] and Konqueror [most recently version 3.5.10] using several Unix-like envs [Linux {Knoppix 5.3.1, Slax 6.0.9, DSL, Gentoo}, OSX {Tiger and Leopard}, and BSD {FreeBSD 7.x, and OpenBSD}]; I consistently received the same results.) Also, upon closer inspection (of the second version)--both using document.write(...) calls and FireBug--I noticed h_i always remained 0 whenever the setInterval(...) code ran.

So, how do I resolve this/these issues?

NOTE: Occasionally, I needed to use \[i\] in both code fragments to prevent the forum parser from treating my indexes as style codes. Please let me know if there's another way to do this (without my needing to rename the index variable, i).

The working code:

<HTML>
<HEAD>
<TITLE>Fader</TITLE>
<SCRIPT LANGUAGE="Javascript">
var hex=new Array('#000000', '#1f1f1f', '#3f3f3f', '#5f5f5f', '#7f7f7f', '#9f9f9f', '#bfbfbf', '#dfdfdf', '#ffffff'), i=0, intervalId;

function fadeIn() {
document.body.style.backgroundColor = hex\[i\];
++i;
if (i>=hex.length) clearInterval(intervalId);
}

// 50 ms is good
[b]intervalId = setInterval('fadeIn();', 50);[/b]
</SCRIPT>
</HEAD>
<BODY></BODY>
</HTML>

The code with the setInterval issues:

<HTML>
<HEAD>
<TITLE>Fade In And Out</TITLE>
<SCRIPT LANGUAGE="Javascript">
// dithered hex patterns array
var hex=new Array(), [b]h_i=0[/b], h_ub, myInt, n=8

function initHex(a, b) {
for(var i=0; i<n; i++) {
var tmp=b*(n-i)/n+a*i/n-1, j=Math.floor(tmp/16), k=tmp%16
hex\[i\]=j.toString(16)+k.toString(16)
}
hex[n]="00"

// assert hex.length is proper value (n+1)
if(hex.length!=(n+1)) document.write('initHex: ERROR: Recheck the bounds i and n.')
} // end of initHex

function setBgColor(i) {
// reinterpret i (a color palette value) as a 3-digit, packed, base rdx (the number of elements in hex) value;
// and split off each digit as a separate index
var rdx=hex.length, j=Math.floor(i/Math.pow(rdx,2)), k=Math.floor((i%Math.pow(rdx,2))/rdx), l=i%rdx
// transform those indexes into the corresponding RGB-value
x='#'+hex[j]+hex[k]+hex[l]
document.body.style.backgroundColor=x
document.write('i: '+i+' backgroundColor: '+x+'<BR>')
} // end of setBgColor

function doIt() { setBgColor([b]h_i++[/b]); if (h_i>=h_ub) { h_i=0; clearInterval(myInt); } }

function fade() {
document.open('text/html')
initHex(0, 0x100)
h_ub=Math.pow(hex.length,3)

// NOTE: I keep getting 'document.body is null' errors in FF 3.0.5 unless
// I first write something, before calling setBgColor(...).
document.write('<BR>')

// [b]myInt=setInterval('doIt();', 50)[/b]
//setBgColor(400) // 400 <--> #7f007f
} // end of fade

fade()
[b]myInt=setInterval('doIt();', 50)[/b]
</SCRIPT>
</HEAD>
<BODY></BODY>
</HTML>

Also, I'd like to know why I kept getting 'document.body is null' errors in FireFox 3.0.5 whenever I attempted to change the background color--unless I wrote something (which could be anything other than ' ' or '\t') to the document stream beforehand.

daveVk

11:26 pm on Dec 30, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



You refer to DOM elements prior to body onload event firing. I think this is the likely cause of 'document.body is null'. Try executing script as body onload event.

The looping issue seems to relate to h_i and h_ub, try writing these to status Line on each interation

Welcome to the forum.

r3dlp

4:29 am on Dec 31, 2008 (gmt 0)

10+ Year Member



Thank you for the welcome.

Actually, I get that 'document.body is null' error message in both cases (including when the script executes via the body onload event).

Also, I displayed h_i directly in the document using document.write. h_i increases (as it's supposed to) whenever setInterval(...) isn't called; however, it never changes from 0 whenever setInterval(...) is called.

daveVk

7:14 am on Dec 31, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



h_i increases (as it's supposed to) whenever setInterval(...) isn't called; however, it never changes from 0 whenever setInterval(...) is called.

I don't follow. No setInterval = no doit() = no h_i++ ?

Does 'document.body is null' go away if you staticly add contents to body like
<body>Hello world</body>

r3dlp

7:31 pm on Dec 31, 2008 (gmt 0)

10+ Year Member



Sorry, perhaps my reply was a bit too terse.

First, I pruned my debug-output statements from the code I posted, and I attempted to explain/describe some of the tests I ran on the code. For example, following are 2 lines of debug code that I pruned:

function doIt() { document.write('h_i: '+h_i+'<BR>'); ++h_i; if (h_i>=h_ub) { h_i=0; clearInterval(myInt); } }
function doIt() { document.write('h_i: '+(h_i++)+'<BR>'); }

In order to test the code without invoking setInterval(...), I'd comment out both calls (one within fade() and the other following the fade() call), and I'd call doIt() directly (actually multiple times via a for-loop) within fade()--placing that statement after the commented-out setInterval(...).

Second, I also noticed that occasionally if I wait awhile, while setInterval(...) sends that code off into the ethers, I'll get a weird stack-trace error message. Also, whether or not I receive the stack-trace, I usually have to stop the execution of the script manually, and I usually have to terminate and reload FireFox (because FF doesn't appear to clean up its program state very well after this issue happens). Unfortunately, since I'm just starting to learn JavaScript, I don't know the internals of JavaScript well enough to dig deeper. (Side note rant . . . this is one of those times I wish I were dealing with C/C++ or ASM code. . . . There has to be some kind of way to transform that setInterval(...) call into into its corresponding C or ASM code--since the compiler and byte-code interpreter has to be able to do this.)

Third, no the 'document.body is null' error doesn't go away even with adding the static content. (Btw, it also might help to mention that I turned on console, script, and net logging in FireBug.)

r3dlp

7:36 pm on Dec 31, 2008 (gmt 0)

10+ Year Member



Btw, why does my thread have a yellow triangle (circumscribing '!') icon next to it--whereas the other ones don't?

Fotiman

9:57 pm on Dec 31, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



One obvious problem that I see is that you're calling document.write in method doIt, which is going to be executed AFTER the page has finished loading (via your setInterval). Calling document.write after the page has finished loading will REPLACE the current document with whatever is "writen". Therefore, you've removed all of your script, all of your content, etc.

document.write is a sloppy way of doing things. Instead, try using DOM methods to insert nodes into the document.

Also, the yellow triangle icon shows you which threads you have posted in (makes it easier to find threads you're interested in).

Hope that helps.

Also, instead of <script language="JavaScript">, the CORRECT script tag is: <script type="text/javascript">. The language attribute is not valid.

daveVk

12:45 am on Jan 1, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Getting rid of document.write statements is good idea, For debug, write to firebug console or the status line, if they are needed operationly use DOM methods as Fotiman suggests.

r3dlp

4:15 am on Jan 1, 2009 (gmt 0)

10+ Year Member



Interesting . . . I learned that syntax (for the script tag) from a book, "Advanced JavaScript 3rd ed" (Wordware Publishing, Inc 2008).

Now, I see--after having checked out the w3c HTML 4.0 and 5.0 standards--that the language attribute was non-standard and was deprecated in the w3c HTML 4.0 standard. (Not that I didn't believe you. I'm a JS n00b [and a specialist in other languages], so I'm just trying to get my bearings--gotta crawl and slurp some Gerber's pureed beans before I'm mature enough to bolt to Chick-fil-a for some waffle fries.)

Concerning the document.write(...), do you mean I should do something like the following?

 function echo(s) {
return [b]document.getElementById("response").appendChild(document.createElement("div")).appendChild(document.createTextNode(s));[/b]
}
</script>
</head>
<body>
<div id="[b]response[/b]"></div>

Anyway, I thought that everything in <HEAD> . . . </HEAD> gets loaded before the onLoad event triggers. In fact the author stated (on pg 95), "You should normally place all functions at the top of the page, or more accurately, in the <HEAD></HEAD> portion of the document. This action forces JavaScript to evaluate your functions before it continues laying out the page. With this practice you guarantee that when the browser comes across an event handler in the HTML portion of the page, it will succeed in calling the function associated with that event if it occurs."

So, basically, do I need to scrap my current approach for invoking setInterval(...) and instead use a closure to implement the behavior I want (with respect to setInterval(...))?

daveVk

5:52 am on Jan 1, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



A workable approach is to execute the js onload using dom functions in lue of document.write()

The echo() function looks Ok

Anyway, I thought that everything in <HEAD> . . . </HEAD> gets loaded before the onLoad event triggers

Loaded yes, executed maybe, but not delayed executions as per setInterval()