Previously: Browser Grading, Historical Browser Detection, Requirements and Loaders, Detection and Fallback.
Code we can be reasonably happy with
Here’s a complete detector/loader:
<link rel="preload" as="style" href="/styles/core.css" />
<link rel="preload" as="style" href="/styles/structure.css" />
<link rel="preload" as="style" href="/styles/content.css" />
<link rel="preload" as="script" href="/scripts/bundle.js" />
<script>
(function (mainStyles, mainScripts, fallbackStyles, fallbackScripts) {
var target = document.getElementsByTagName('script')[0];
var head = target.parentNode;
var mode;
function insertStyle(src) {
document.write('<link rel="stylesheet" href="' + src + '">');
}
function insertScript(src) {
var s = document.createElement('script');
s.setAttribute('src', src);
head.insertBefore(s, target);
}
try {
if (window.URL && window.URLSearchParams) {
var url = new URL(document.URL);
mode = url.searchParams.get('mode');
if (mode == 'fallback') {
throw new Error('fallback mode requested');
}
}
if (mode != 'broken') {
var f = new Function('import("test dynamic imports")');
for (var i = 0; i < mainStyles.length; i++) {
var ss = mainStyles[i];
insertStyle(ss);
}
for (var i = 0; i < mainScripts.length; i++) {
var s = mainScripts[i];
insertScript(s);
}
}
} catch (e) {
for (var i = 0; i < fallbackStyles.length; i++) {
var ss = fallbackStyles[i];
insertStyle(ss);
}
for (var i = 0; i < fallbackScripts.length; i++) {
var s = fallbackScripts[i];
insertScript(s);
}
}
})(
['/styles/core.css', '/styles/structure.css', '/styles/content.css'],
['/scripts/bundle.js'],
['/styles/fallback.css'],
['/scripts/fallback.js']
/* Don't autoformat this file. If the formatter adds a trailing comma it will break IE */
);
</script>
<script>/* We need this script to prevent a FOUC */</script>
What’s new in this version? Well, it’s wrapped in a function expression so it’s not making a bunch of global variables. It can load multiple scripts and styles. It can load scripts and styles in the fallback case. (Just make sure they’re written to work in Netscape 4!)
Because we’re allowing scripts and styles in the fallback path, we also support ?mode=broken, which loads nothing at all, so you can check how it will look both IE9 and IE3. (IMO, this is excessive, and I wouldn’t allow fallback scripts or styles if I ran the zoo.)
We’ve also included preload tags for the files that competent browsers want. That means they’ll start downloading right away. This is straight out of a project I was working on which built to a plain script; If your JavaScript is served as a module, make sure to use modulepreload for your module instead.
Note also the two comments near the end. A lot of code formatters like to leave trailing commas on parameter lists, which helps make diffs more readable. This is totally legal in JavaScript, but not in Microsoft JScript. You’ll get a syntax error in IE if you do that.
The other thing is the empty </script> tag at the end. It turns out that Firefox only blocks rendering and waits for stylesheets if you do this. It doesn’t have to be empty, which is why you almost never see it these days, but when you document.write() your stylesheet, there needs to be some script tag to trigger layout blocking.
I spent a lot of time debugging this guy in BrowserStack.