IndiePaivaGame / swfobject

Automatically exported from code.google.com/p/swfobject
0 stars 0 forks source link

Discuss alternative approach for IE DomContentLoaded emulation #165

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Discuss if an alternative approach for IE DomContentLoaded emulation is a
better aletrnative:
http://javascript.nwbox.com/IEContentLoaded/

Original issue reported on code.google.com by bobbyvandersluis on 31 Aug 2008 at 11:19

GoogleCodeExporter commented 9 years ago
Also, semi-related to issue #162, I think the reliance on a document.write
('script...deferred...') type of IE DomContentLoaded emulation is bound to 
cause 
problems if swfobject is dynamically added to a page that is already loaded, as 
document.write() commands have been known to overwrite some or all of the 
existing 
DOM in certain circumstances.

I think the onScroll type trick is clever and has some good possibilities to it.

Original comment by get...@gmail.com on 5 Sep 2008 at 4:38

GoogleCodeExporter commented 9 years ago
Another negative argument: 
http://code.google.com/p/swfobject/issues/detail?id=209

Original comment by bobbyvandersluis on 4 Nov 2008 at 9:39

GoogleCodeExporter commented 9 years ago
In SWFObject 2.2 alpha 1 we have implemented a new method to simulate
DomContentLoaded event in Internet Explorer. It is based on Diego Perini's 
solution [
http://javascript.nwbox.com/IEContentLoaded/ ], which has also be included in
libraries like jQuery.

Advantage of this approach is:
- inline detection instead of relying on an ugly detection method that includes
document.write and an invalid path

Possible disadvantage of this approach is:
- when you include the library in an iframe it will only fire on onload of the 
loaded
document

Original comment by bobbyvandersluis on 18 Nov 2008 at 4:37

GoogleCodeExporter commented 9 years ago
Hey Bobby...nice addition to cover MS faults.

A small suggestion for the IEContentLoaded addition you recently made:

    // Internet Explorer on Windows
    if (ua.ie && ua.win) {
        document.attachEvent('onreadystatechange',
            function() {
                if (document.readyState == 'complete') {
                    document.detachEvent('onreadystatechange', arguments.callee);
                    callDomLoadFunctions();
                }
            }
        );
        // not inside an iframe
        if (win == top) {
            timer = setInterval(function() {
                try {
                    doc.documentElement.doScroll("left");
                } catch(e) {
                    return;
                }
                callDomLoadFunctions();
            }, 10);
        }
    }

The 'onreadystatechange' is needed to ensure IE fires events in correct order, 
also
when pages do not contain binary assets or those assets are very small or 
cached.

The idea is that both the "doScroll" and "onreadystatechange" events will fire, 
we
just serve the first to come and forget the other. Depending on the properties 
of the
loaded page the order in which the two will fire is not predictable, but is not 
so
important for the task of the DOMContentLoaded issue.

What instead is very important is that IEContentLoaded should always precede 
"onload"
event. Without the "onreadystatechange" event you can not ensure/enforce this 
order
100% of the time.

Also, you have to make sure "callDomLoadFunctions()" is only called once by 
using a flag.

It is necessary to move the "callDomLoadFunction()" out of the try/catch block,
elsewhere some javascript error my remain hidden to developers (trapped in the 
try).

Hope it helps overcome that nasty IE bug. It works for IE8 too and is important 
since
that is still an open issue for Microsoft (after many years).

One last thing... the original solution used setTimeout, don't know is 
setInterval
can introduce notable behavior differences or leaks on IE. You just know where 
to
look in case this happen in your tests.

Feel free to contact me should you have any questions or suggestions about it.

Thank you for using it and spread the word.

--
Diego Perini

Original comment by diego.pe...@gmail.com on 25 Nov 2008 at 8:21

GoogleCodeExporter commented 9 years ago
Hi Diego, thanks for your comments, and for finding a great solution of course 
:-)

Regarding onreadystatechange, Mark Wubben and Dean Edwards researched this 
event,
Mark even documented it [
http://novemberborn.net/javascript/domcontentloaded-style-information ], and 
one of
his main conclusions was:

'It turns out that in Internet Explorer document.readyState fires after the 
loading
of the image, and right before window.onload. In effect, it’s useless. The
“official,” script.readyState variant does fire before the image loads, and 
is indeed
the correct version.'

Like jQuery, SWFObject already has a cross-browser unload function as fallback, 
so
personally I don't see the use in also adding the onreadystatechange event. Am I
missing something here?

Regarding moving the "callDomLoadFunction()" out of the try/catch block, that's 
a
good one, I've updated this for the next alpha.

Regarding setInterval vs setTimeout question. John Resig has written a post 
about it
a while ago: [ http://ejohn.org/blog/how-javascript-timers-work/ ]. One of his
conclusions was that:

'setTimeout(function(){
    /* Some long block of code... */
    setTimeout(arguments.callee, 10);
  }, 10);

  setInterval(function(){
    /* Some long block of code... */
  }, 10);

These two pieces of code may appear to be functionally equivalent, at first 
glance,
but they are not. Notably the setTimeout code will always have at least a 10ms 
delay
after the previous callback execution (it may end up being more, but never less)
whereas the setInterval will attempt to execute a callback every 10ms 
regardless of
when the last callback was executed.'

So I think you're right, from an execution point of view setTimeout will be a 
less
processor taxing and a bit cleaner solution, because it doesn't queue up its own
calls. Will change this for the next alpha too.

If you have any additional comments, please let us know.

Original comment by bobbyvandersluis on 26 Nov 2008 at 10:36

GoogleCodeExporter commented 9 years ago
'cross-browser unload' -> onload, of course ;-)

Original comment by bobbyvandersluis on 26 Nov 2008 at 10:37

GoogleCodeExporter commented 9 years ago
SWFObject 2.2 alpha 3 contains a series of updates (comments 4-6).

Original comment by bobbyvandersluis on 26 Nov 2008 at 2:37

GoogleCodeExporter commented 9 years ago
Bobby,
I completely agree and recognize as valid those test you mention. The
"onreadystatechange" fires after the images are loaded, but the images or flash 
may
be already cached and retrieving them will be really fast in today browsers.

The "onreadystatechange" is there also to catch those situations (most of the 
time
except the first visit to the page). The first time the "doScroll()" trick will 
do
the job, when everything is cached the standard Microsoft event will do the job.

Notice that Mark and Dean concluded:

'It turns out that in Internet Explorer document.readyState fires after the 
loading
of the image, and right before window.onload.'

So the part we are interested in is exactly "it fires before onload". That's the
trick we were missing. Since it fires before "onload" it is good enough for us 
to
keep a consistent load order on all browser.

Otherwise the "onload" event will sometime fire before on IE, and the worst is 
the it
will do that only on IE. So if you have different things starting at READY time 
and
others at ONLOAD time you will find yourself with different behavior in 
different
browsers.

I published some test cases showing the problem both on my lab site and on 
various
lists like jQuery and Prototype showing the problem. This technique is 
currently used
by Google, jQuery, ExtJS and many other libraries out there.

jQuery has currently accepted the patch related to the "onreadystatechange" 
event,
the ticket related to this was #2614.

Not that you should blindly follow what the other do, but if you want to try it
yourself put a console.log() line in both methods and see when each of them 
does the
job. You will realize that "doScroll()" is mostly useful only at the very first 
visit
alll the other cached hits should be routed through "onreadystatechange".

This is especially visible when there are no images or external binaries in the 
file
but this is only to be able to see the problem easily, there are many other
situations that trigger that special behavior in IE like the Back/Forward 
buttons,
Reload button, Refresh Menu Item and also presence of FORMs in the HTML.

The real problem with my solution is that the "doScroll()" method does not work 
in
iframes, so the "onreadystatechange" is useful here too, it will fire a bit 
late in
iframes but with a higher degree of consistency with other browser. The other 
way is
just too risky for most developers. The "document.write" will overwrite your 
content
first or later, that solution seems too tighten to network delays.

Hope these info may be of help, you can find more info and talks about it on the
jQuery list or in the ExtJS forum.

Let me know if I can be of further help with this.

--
Diego

Original comment by diego.pe...@gmail.com on 27 Nov 2008 at 3:17

GoogleCodeExporter commented 9 years ago
Ok, swfobject 2.2 alpha4 now has the following functionality:

    var onDomLoad = function() {
        if (!ua.w3cdom) { return; }
        if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof
doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) 
{ //
function is fired after onload, e.g. when script is inserted dynamically 
            callDomLoadFunctions();
        }
        if (!isDomLoaded) {
            if (typeof doc.addEventListener != UNDEF) {
                doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
            }       
            if (ua.ie && ua.win) {
                doc.attachEvent("onreadystatechange", function() {
                    if (doc.readyState == "complete") {
                        doc.detachEvent("onreadystatechange", arguments.callee);
                        callDomLoadFunctions();
                    }
                });
                if (win == top) { // if not inside an iframe
                    (function(){
                        if (isDomLoaded) { return; }
                        try {
                            doc.documentElement.doScroll("left");
                        }
                        catch(e) {
                            setTimeout(arguments.callee, 0);
                            return;
                        }
                        callDomLoadFunctions();
                    })();
                }
            }
            if (ua.webkit) {
                (function(){
                    if (isDomLoaded) { return; }
                    if (!/loaded|complete/.test(doc.readyState)) {
                        setTimeout(arguments.callee, 0);
                        return;
                    }
                    callDomLoadFunctions();
                })();
            }
            addLoadEvent(callDomLoadFunctions);
        }
    }();

I think that this is exactly in line with your comments, not?

Original comment by bobbyvandersluis on 27 Nov 2008 at 1:00

GoogleCodeExporter commented 9 years ago
Good work Bobby, the IE part seems complete to me, at least as I depicted it.

Put it under test with some of the cases you already have about this specific IE
misbehaviors and let me know if it is able to fix the known failing tests or 
not.

Hope this helps mitigate some of the mess of the most hated browser.

--
Diego

Original comment by diego.pe...@gmail.com on 27 Nov 2008 at 8:19

GoogleCodeExporter commented 9 years ago
Included in the SWFObject 2.2 beta1 release

Original comment by bobbyvandersluis on 16 Apr 2009 at 3:05