Learnings from researching fault tolerent CSS delivery
Whilst researching fault tolerant CSS delivery I went down a number of blind alleys and, whilst they didn't end up as part of the final post, I'm sharing some of them here out of interest.
Caveat lector: Note that I haven't investigated these as thoroughly as I normally would before I post here.
<link rel="stylesheet">
and DOMContentLoaded
Adding a stylesheet by injecting <link rel="stylesheet">
before the DOMContentLoaded
event will block DOMContentLoaded
until the stylesheet request completes. This is a problem if your JavaScript is waiting for DOMContentLoaded
before it executes: you don't have all the styles and you don't have your JavaScript running.
Injecting a <link>
using the DOMContentLoaded
event is too late, there's a noticeable flash of partially styled content. It will also delay the page load
event.
Injecting a <link>
element does not seem to completely block script execution if done after DOMContentLoaded
but I've not in any way systematically tested this - it's just something I think I noticed whilst trying to sort out other problems.
<script href="…" async>
and DOMContentLoaded
If you set the async
flag on an external script then there's a real probability* that the script will execute after the DOMContentLoaded
event has fired. If you are binding your JavaScript initialisation code to this event then it will never run. You'll need to check the state of the DOM first:
if (document.readyState !== 'loading') {
// DOMContentLoaded has already fired.
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
* I.e. It's going to happen. Maybe not every time, but once is enough in this case.
Detaching from events
If you add a time out to try and detach your code from running as part of an event then I found I needed to set the time out to at least 100ms. (I say "at least" but 100ms worked in Firefox and Chrome and I didn't systematically test it until I found the lowest possible value.)
// This will run as part of the event and delay it's completion.
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function () {
// Code that takes a long time to complete.
}, 1);
});
// This will run after the event has completed.
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function () {
// Code that takes a long time to complete.
}, 100);
});