- From: Jonas Sicking <jonas@sicking.cc>
- Date: Tue, 7 Jul 2009 00:20:56 -0700
Hi all! I wanted to share some experiences with implementing the defer attribute on scripts. After the initial implementation[1] one of the problems that we quickly ran in to was pages going blank [2][3][4] due to pages containing markup like <script defer> document.write("something"); </script> In IE this will in some circumstances (more below) not replace the current page, but instead just append the written markup to the end of the page. There was even one large site that had markup like <script defer> if (navigator.platform == "MacIntel") { document.write("something"); } </script> even though as far as I know there is no browser running on mac that supports defer (maybe Opera?). What's tricky is that a document.write inside a deferred script in IE will in some circumstances clear the current document, in other cases append to it. Specifically it seems that if the page has ever set .innerHTML on any node then document.write in the deferred script will append to the current page. If .innerHTML has never been set, then it will replace the current page. In order to fix this in Gecko we decided [2] to let the "insertion point" be set to the end of the document while executing all deferred scripts. Additionally we hold off firing DOMContentLoaded until all deferred scripts have loaded and executed. This was in part due to implementation specific problems (a lot of internal mozilla code relies DOMContentLoaded), but also to allow pages to more easily defer scripts across browsers [5]. Additionally always firing DOMContentLoaded before all, or after all, deferred elements increases consistency and reduces the risk of race conditions in pages. Also, in an effort to reduce the amount of possible race conditions in pages, if there are any scripts that were inserted using the DOM, we wait for all those to finish loading and executing before firing DOMContentLoaded. This was mostly because in gecko the concept of "insertion points" works differently and so document.write never clears the current document before DOMContentLoaded has fired, making races with DOMContentLoaded particularly noticeable. Another thing where it appears that the spec differ from Geckos behavior is nodes that are not async and not deferred. Gecko always executes these in the order they are inserted into the main Document. So if two <script> nodes are created, one with a src attribute set, and another with just inline script, and then inserted into the Document, they are always executed in insertion order no matter of the src-script or the inline-script is inserted first. All of the above is how things work in Firefox 3.5. In addition to this we in the next version of firefox where document.readyState is supported, hold off setting document.readyState to "interactive" until all deferred scripts have executed. It appears that the spec changes readyState before executing deferred scripts which I think would be fine to change our implementation to do. So all in all, I'd like to see the following changes to the spec: * Don't fire DOMContentLoaded until all deferred scripts have executed. * While executing deferred scripts, let the "insertion point" be at the end of the document. * Always execute elements in the order they are inserted into the Document, with exception of async and deferred scripts. * Possibly hold off firing DOMContentLoaded until any outstanding scripts have finished loading and executing. [1] https://bugzilla.mozilla.org/show_bug.cgi?id=28293 [2] https://bugzilla.mozilla.org/show_bug.cgi?id=461555 [3] https://bugzilla.mozilla.org/show_bug.cgi?id=461724 [4] https://bugzilla.mozilla.org/show_bug.cgi?id=469751 [5] https://bugzilla.mozilla.org/show_bug.cgi?id=474392 / Jonas
Received on Tuesday, 7 July 2009 00:20:56 UTC