So you want to insert a bit of javascript on your DNN page to perform some function that you probably should code into a module but you know you can implement more quickly by dropping script into a Text/HTML module (or in the Header/Footer of any module's Advanced Settings). The problem is, your script fires at the wrong time -- before the page is fully loaded. Here's a way to insure your script always waits until the DNN page is loaded...
The solution relies on the premise of "javascript closure". That topic is too big to tackle in this post but you can read up on it here: http://www.jibbering.com/faq/faq_notes/closures.html
In short, closure lets you begin execution of a function that is not fully bound until the page is fully loaded, therefore the function cannot actually execute until the page is fully loaded. The function gets queued during page load and does not execute until page load is complete. Cool, huh?
Just gimmie the solution
Sorry, I forgot -- you're here because you probably wanted to implement something FAST without stopping to code a module. Ok, here's the solution with thanks to Simon Willison (http://simonwillison.net/). Simon coded the short solution we'll use.
Our solution is to use a helper function to call our target javascript code. The helper function acts as a wrapper to take advantange of javascript's closure feature. Our target code will be an uber-simple function to open an alert box that says "Page has loaded":
Now on to our "closure" helper function. Again, "closure" is a big topic so I won't explain the function other than how to use it. Follow my previous link to read all about identifier resolution, execution context and scope chains and the following function might begin to make sense.
Here's the helper function courtesy of Simon Willison:
function addLoadEvent(func)
{ var oldonload = window.onload;
if (typeof window.onload != 'function')
{ window.onload = func; }
else
{ window.onload = function()
{ oldonload();
func();
}
}
}
To use the helper, call it with the name of our target function:
addLoadEvent(msgPgLoaded);
Now our 'msgPgLoaded' function will not fire until the page is fully loaded. Perfect!
Call addLoadEvent with inline script
For our simple example of an alert box we could have also called our helper function and passed the actual code we wanted to execute. That would save us from having to include a separate target function. Here's the same task accomplished by passing code in the addLoadEvent call:
addLoadEvent(function() { alert('Page has loaded') });
Real-world example
How is this closure ability useful to the common DNN coder? I'll give you the very example that led me to implement my first closure helper function in a DotNetNuke environment:
A client required a "dashboard" view of several unreleated groups of data. Some data was culled from input form responses while other data was queried from various DNN database tables. In all, a very busy page with a lot of data. The customer was somewhat overwhelmed by the resulting amount of data but insisted on having everything on one page. We determined that a single reporting page would be sufficient IF the customer were given a simple pop-up box alert when certain data met certain criteria.
The issue we now faced was how to quickly deliver the requested alert functionality consistently across multiple module types (including some 3rd-party modules). Added to this, some of the requested alert logic required a comparison of results across multiple modules.
Since we're talking about javascript you can guess that our answer was to write a small javascript parser to compare the various modules' output data and pop alerts when appropriate. We placed our code inside a hidden Text/HTML module at the bottom of our reporting page. This worked well until we re-arranged the positioning of modules on the page -- suddenly our lovely javascript solution began popping alerts before data was loaded! Because of our module re-arrangement DNN was loading our hidden script module before some of the data reporting modules it depended on.
We could have just re-positioned our hidden script module until it was the last module loaded but we wanted a solution that could survive a simple module move or a skin change. The eventual answer was to employ the same closure helper function outlined above.
Last word: Beware multiple addLoadEvent calls
Oooh you knew this was coming, didn't you? As enticing as this solution is, coders still must be careful when making more than one addLoadEvent call on a page. Why? Because some functions may take actions that prevent other functions from executing. A classic example is having two functions you want to execute back-to-back. If function #1 happens to result in a postback then function #2 never has a chance to execute! Be mindful of all the possible functional paths your closure-enabled scripts can take.