![]() |
|
|
|||||||
![]() |
|
|
Thread Tools | Rate Thread |
|
|
PM User | #1 |
|
Senior Coder ![]() ![]() Join Date: Jun 2002
Location: near Oswestry
Posts: 4,509
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
Using the DOM before window.onload (Part II)
There was a part one, but I can't find the thread now...
Anyway, brief precis - DOM scripts can't be called before window.onload, because you can't rely on the DOM being useable until it's closed (the parser has finished parsing the document). However the window.onload event is synchronous with images and external dependencies, so it doesn't fire until they've all resolved (or timed out). What we really want is an event that fires when the DOM ready, without reference to external dependencies. But there isn't one. I'd been messing about with image onload events - trying to introduce some server latency in order to buy the time we need between the rendering of an object at the end of the </body> (the image with the onload handler) and the DOM being ready. It works, but it's a messy hack, and anyway <img onload> has been deprecated in XHTML. So, then I thought ... what about a timer that checks for the document and then a DOM method, and if it exists we know the DOM is ready so we can safely call our function. Like this [where the eventual function we're calling is iotbs()] Code:
//DOM-ready initialisation call object (timeout-counter, wait function)
var domReady = { 'time' : 0, 'wait' : window.setInterval(function()
{
//increment the timeout-counter
domReady.time ++;
//if we get to 100 (10 seconds)
if(domReady.time >= 100)
{
//timeout the request
clearInterval(domReady.wait);
}
//otherwise if the document and a DOM method exists
else if(typeof document != 'undefined' && typeof document.getElementsByTagName != 'undefined')
{
//call IOTBS initialisation function, if it exists
if(typeof iotbs == 'function') { iotbs(); }
//timeout the request
clearInterval(domReady.wait);
};
},100) };
__________________
"Why bother with accessibility? ... Because deep down you know that the web is attractive to people who aren't exactly like you." - Joe Clark Last edited by brothercake; 04-07-2005 at 11:36 AM.. |
|
|
|
|
|
PM User | #2 |
|
Regular Coder ![]() Join Date: Aug 2004
Location: codegoboom@yahoo.com
Posts: 999
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
I don't know, but isn't the DOM available when a script is placed just prior to </body>?
__________________
*this message will self destruct in n-seconds* |
|
|
|
|
|
PM User | #3 |
|
Red Devil Mod ![]() ![]() Join Date: Apr 2003
Location: Bucharest, ROMANIA
Posts: 6,842
Thanks: 49
Thanked 219 Times in 215 Posts
![]() ![]() |
a silly question... is it 100 equal with 10 seconds? Aren't there 100 miliseconds ( 0.1 seconds)?
On the other hand... same quest as codegoboom: is'n DOM available anyway when prior to content? |
|
|
|
|
|
PM User | #4 | ||
|
Senior Coder ![]() ![]() Join Date: Jun 2002
Location: near Oswestry
Posts: 4,509
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
Quote:
![]() But now I think again, maybe 15 seconds would be better. Quote:
But anyway .. if you try manipulating the DOM before then it might fail; in most cases it actually won't fail, it will work fine - but that's just luck - interpretor latency and similar delays, which are not predictable in our favour.
__________________
"Why bother with accessibility? ... Because deep down you know that the web is attractive to people who aren't exactly like you." - Joe Clark Last edited by brothercake; 04-07-2005 at 11:43 AM.. |
||
|
|
|
|
|
PM User | #5 |
|
Red Devil Mod ![]() ![]() Join Date: Apr 2003
Location: Bucharest, ROMANIA
Posts: 6,842
Thanks: 49
Thanked 219 Times in 215 Posts
![]() ![]() |
yeap. sounds logical...
Now, with no direct link with the thread, I wish to ask something else. I remember that you (or it was not you? - I am not able to find that thread again) showd us a tricky way to a real preloader of images. I have noticed that all the preloading code I have seen looks and acts on the presumtion that onload will fire the function when the whole page is loaded. I guess that most of the time the page loading time is not the same with full images loading time, so that those prelaoding codes are not relyable to me... Is there a true method to verify if a SRC element has been really loaded into the cache? |
|
|
|
|
|
PM User | #6 |
|
Red Devil Mod ![]() ![]() Join Date: Apr 2003
Location: Bucharest, ROMANIA
Posts: 6,842
Thanks: 49
Thanked 219 Times in 215 Posts
![]() ![]() |
...and I remeber that I have recently noticed that thare is even a difference between browsers in loading sequencely the DOM
http://www.codingforums.com/showthread.php?t=55690 |
|
|
|
|
|
PM User | #7 |
|
Senior Coder ![]() ![]() Join Date: Jun 2002
Location: near Oswestry
Posts: 4,509
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
Part IIb - The Rewrite
Okay .. I've had to restructure this construct quite a lot - the way it was before it was causing a crash bug in IE5.0 on Windows 2K and 98SE. It was the use of setInterval that was doing it - setTimeout was not a problem - so I've changed it to use that, but that of course means restarting the timer manually, so I need a reference to it, and hence a named (rather than anonymous) function. I've used the timeout syntax ("function()", n) rather than (function, n), because the latter doesn't work in Safari 1.0
I also increased the attempt time to 15 seconds, and slowed it down to 250ms iterations to be less intensive. And I've added another object test, for the body element, so that the scripting can go in the head section, and because without that there were occassional errors in IE and Konqueror from the body not existing (as I suppose there would be). Here's the revised code. Anything else I haven't thought of that could make it untenable? Code:
//DOM-ready watcher
function domReady()
{
//start or increment the counter
this.n = typeof this.n == 'undefined' ? 0 : this.n + 1;
//if the document, a DOM method, and the body all exist
if(typeof document != 'undefined' && typeof document.getElementsByTagName != 'undefined' && document.getElementsByTagName('body')[0] != null)
{
//>>>-------------------------<<<
//>>> DOM SCRIPTING GOES HERE <<<
//>>>-------------------------<<<
}
//otherwise if we haven't reached 60 (so timeout after 15 seconds)
else if(this.n < 60)
{
//restart the watcher
setTimeout('domReady()', 250);
}
};
//start the watcher
domReady();
__________________
"Why bother with accessibility? ... Because deep down you know that the web is attractive to people who aren't exactly like you." - Joe Clark Last edited by brothercake; 04-12-2005 at 09:02 PM.. |
|
|
|
|
|
PM User | #8 |
|
Senior Coder ![]() ![]() Join Date: Jun 2002
Location: near Oswestry
Posts: 4,509
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
Oh and another thing - if the scripting is going to modify any pre-existing HTML elements, the test condition also has to check for that:
Code:
//DOM-ready watcher
function domReady()
{
//start or increment the counter
this.n = typeof this.n == 'undefined' ? 0 : this.n + 1;
//if the document, a DOM method, and the body all exist
//>>> and check for any pre-existing elements the script needs: && obj != null
if
(
typeof document != 'undefined'
&& typeof document.getElementsByTagName != 'undefined'
&& document.getElementsByTagName('body')[0] != null
&& document.getElementById('something') != null
)
{//>>>-- DOM SCRIPTING GOES HERE --<<<
}//>>>-----------------------------<<<
//otherwise if we haven't reached 60 (so timeout after 15 seconds)
else if(this.n < 60)
{
//restart the watcher
setTimeout('domReady()', 250);
}
};
//start the watcher
domReady();
__________________
"Why bother with accessibility? ... Because deep down you know that the web is attractive to people who aren't exactly like you." - Joe Clark |
|
|
|
|
|
PM User | #9 |
|
Senior Coder ![]() Join Date: Aug 2004
Location: Twin Cities
Posts: 1,345
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
I do this:
Code:
function Loader() {
this.action = [];
this.add = function(obj) {
this.action.push(obj);
}
this.exec = function() {
var i;
for (i in this.action) {
this.action[i]();
}
}
}
var loader = new Loader();
window.onload = function() { loader.exec(); };
Code:
loader.add(function() {
/* you can do this multiple times. */
});
Last edited by fci; 04-16-2005 at 05:34 AM.. |
|
|
|
|
|
PM User | #10 |
|
Senior Coder ![]() ![]() Join Date: Jun 2002
Location: near Oswestry
Posts: 4,509
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
A neat technique
But it still has the same core "problem" as all such wrappers - it doesn't fire until images and other dependencies have resolved or timed out, so if you have a less-than-reliable ad server, or your pages are just graphics heavy, you can be sitting there waiting for your scripts to start, when they could have started already ... if only they'd known ..That's the issue I'm trying to address I've been doing a lot of tweaking and refinement on this idea, putting it into practise with different scripts, and chatting with jkd about it, and I've discovered some interesting things: I'd been assuming that an element object doesn't exist until its close tag has rendered, but in fact that's not the case - an object exists as soon as its open tag exists, so a test inside an element can test for the element itself. This is fab - it means that the typeof tests for "document" and "getElementsByTagName" are unecessary, I can go straight to testing for the BODY. However I did have to keep the typeof test anyway, because that's what filters out non-DOM browsers. Okay, so then I discovered that in older gecko builds (testing ns7.1), if the script is in the HEAD section, the reference "document.getElementsByTagName('body')" is always a collection with zero items, and item(0) is always "undefined" (not null, curiously). If the script is put in the BODY, it can retrieve the BODY using getElementsByTagName; but in the HEAD it can only retrieve the HEAD (even after 60 iterations, and the page has long since stopped processing anything else) But even stranger, although this is the case, it's not the case for the reference "document.body" - that references comes back as and when expected. Bizarre. So, I had to supplement the test for getElementsByTagName('body') with this additional test for document.body, arranged so that if document.body doesn't exist at all (eg, in some browsers in XHTML mode) then it won't err or break, it will just use the first test; it will always use the first test, unless that fails; so the only gap there is old gecko builds in XHTML mode - but that's not really an issue, partly because those older builds have largely fallen by the wayside, but mostly because they retain that particular shortcut object while in XHTML mode anyway. So no worries sheila ![]() Ho hum .. enough of my rambling Here's the new code, first with original whitespace and commenting:Code:
//DOM-ready watcher
function domReady()
{
//start or increment the counter
this.n = typeof this.n == 'undefined' ? 0 : this.n + 1;
//if DOM methods are supported, and the body element exists
//(using a double-check including document.body, for the benefit of older moz builds [eg ns7.1]
//in which getElementsByTagName('body')[0] is undefined, unless this script is in the body section)
//>>> and any elements the script is going to manipulate exist
if
(
typeof document.getElementsByTagName != 'undefined'
&& (document.getElementsByTagName('body')[0] != null || document.body != null)
//>>> && document.getElementById('something') != null
)
{
//>>>-- DOM SCRIPTING GOES HERE --<<<
alert("The DOM is ready!");
//>>>-----------------------------<<<
}
//otherwise if we haven't reached 60 (so timeout after 15 seconds)
//in practise, I've never seen this take longer than 7 iterations [in kde 3.2.2
//in second place was IE6, which takes 2 or 3 iterations roughly 5% of the time]
else if(this.n < 60)
{
//restart the watcher
//using the syntax ('domReady()', n) rather than (domReady, n)
//because the latter doesn't work in Safari 1.0
setTimeout('domReady()', 250);
}
};
//start the watcher
domReady();
Code:
function domReady(){this.n=typeof this.n=='undefined'?0:this.n+1;if(typeof document.getElementsByTagName!='undefined'&&(document.getElementsByTagName('body')[0]!=null||document.body!=null))
{
alert("The DOM is ready!");
}
else if(this.n<60){setTimeout('domReady()',250);}};domReady();
__________________
"Why bother with accessibility? ... Because deep down you know that the web is attractive to people who aren't exactly like you." - Joe Clark Last edited by brothercake; 04-17-2005 at 05:32 AM.. |
|
|
|
|
|
PM User | #11 |
|
Senior Coder ![]() Join Date: Aug 2004
Location: Twin Cities
Posts: 1,345
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
in my case, I could move this call to the to the bottom(to before the </body> tag or whatever is 'standard'), loader.exec(), or would that not solve the problem?
Last edited by fci; 04-17-2005 at 07:55 AM.. |
|
|
|
|
|
PM User | #12 |
|
Senior Coder ![]() ![]() Join Date: Jun 2002
Location: near Oswestry
Posts: 4,509
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
Well, before this thread I would have said no, that won't work - but in fact it probably will, contrary to what I thought before: if your script is immmediately before the </body> tag and doesn't manipulate any other elements, you should be fine.
The advantages of my method are its rechecking - so if an object doesn't exist it can wait and try again - and the fact that it can go anywhere in the document.
__________________
"Why bother with accessibility? ... Because deep down you know that the web is attractive to people who aren't exactly like you." - Joe Clark Last edited by brothercake; 04-18-2005 at 03:37 PM.. |
|
|
|
|
|
PM User | #13 |
|
Senior Coder ![]() Join Date: Aug 2004
Location: Twin Cities
Posts: 1,345
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
have you thought of allowing your domReady function to accept a function as an argument(similar to how mine does) ? I deal more with dynamic(php, bleh) stuff. if I did static pages(for example) then your function would make more sense to me (just trying to explain my perception of the problem)
|
|
|
|
|
|
PM User | #14 | |
|
Senior Coder ![]() ![]() Join Date: Jun 2002
Location: near Oswestry
Posts: 4,509
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
Quote:
So a construct that could be used like this: Code:
domReady(funcName); Code:
domReady(function()
{
});
__________________
"Why bother with accessibility? ... Because deep down you know that the web is attractive to people who aren't exactly like you." - Joe Clark |
|
|
|
|
|
|
PM User | #15 |
|
Senior Coder ![]() Join Date: Aug 2004
Location: Twin Cities
Posts: 1,345
Thanks: 0
Thanked 0 Times in 0 Posts
![]() |
yes. your current script isn't flexible enough for the dynamic needs that exist(in my case, at least). I'll probably add a DOM check in what I wrote as a result of this thread. you'll also need to modify your setTimeout call.
|
|
|
|
![]() |
| Bookmarks |
| Thread Tools | |
| Rate This Thread | |
|
|