There are a bunch of different bugs in IE which manifest themselves as a page which doesn't finish loading and displays "X items remaining" (or "1 item remaining") in the status bar. C.M. Palmer on Stack Overflow has a pretty good answer for one of these bugs. I recently found an answer to another.
One of the symptoms of this particular bug (and probably others) is that it only happens in certain environments. I finally got access to one client's environment where this happened, and was able to track it down. Intermittently, and for no apparent reason, IE would fail to load the following script:
<script defer src=//:></script>
Yes, that's a script
element with a src
of "//:"
. This ugly hack is actually the best way to simulate the DOMContentLoaded
event in IE (the event which signals that the HTML page has been parsed and its document object-model is fully built), as Dean Edwards and friends have figured out (they also tried src
values of javascript:void(0)
, javascript:false
, and //0
before settling on //:
).
Fortunately, there's another trick that works for this purpose, as Diego Perini discovered: skip this ugly script
element, and poll to check if document.body.doScroll()
is working yet. The downside is that because it requires a timer to call doScroll()
every X milliseconds, you won't be notified of the DOM's readiness quite as quickly as when using the "//:"
hack (whereby you listen for onreadystatechange
events on the script
element — so you're notified of the readiness immediately, rather than having to poll for it).
The upside, of course, is that the doScroll()
method works more reliably, with no "1 item remaining" for which to wait indefinitely. Another problem I had noticed in the past with the script
element method was that IE sometimes would fire the onreadystate complete
event before the DOM was loaded (and I had worked around that by checking if the page's footer element existed, and if not, waiting until it did). I haven't found any holes with this doScroll()
technique yet, though, so I'm happy so far.
The 1.6.x version of the Prototype JavaScript Framework used the script
technique (and I'm sure other libraries have, or still do); the 1.7 version has been updated with the doScroll()
method. Here's the specific code it uses for the doScroll()
technique (I've added comments where it uses Prototype-specific code, in case someone wants to adapt it to a non-Prototype environment):
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
var timer;
function fireContentLoadedEvent() {
if (document.loaded) return;
if (timer) window.clearTimeout(timer);
document.loaded = true;
// raise the Prototype dom:loaded function
// (your custom ondomcontentloaded code goes here)
document.fire('dom:loaded');
}
function checkReadyState() {
if (document.readyState === 'complete') {
// Prototype for removeEventListener()/detachEvent()
document.stopObserving('readystatechange', checkReadyState);
fireContentLoadedEvent();
}
}
function pollDoScroll() {
try { document.documentElement.doScroll('left'); }
catch(e) {
// Prototype for setTimeout()
timer = pollDoScroll.defer();
return;
}
fireContentLoadedEvent();
}
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
} else {
// Prototype for addEventListener()/attachEvent()
document.observe('readystatechange', checkReadyState);
if (window == top)
// Prototype for setTimeout()
timer = pollDoScroll.defer();
}
// Prototype for addEventListener()/attachEvent()
Event.observe(window, 'load', fireContentLoadedEvent);
})();