Reputation: 4965
I have these scripts that I want to download/execute asynchronously to prevent page render blocking:
<script src="jQuery CDN" defer></script>
<script src="Bootstrap CDN" defer></script>
Bootstrap depends on jQuery, so they have to be executed in this order. That's why I'm using defer
, but my question applies for async
as well.
I have seen this related question on Stackoverflow, but these solutions don't work because:
document.write
, which cannot be used for async
/defer
scripts; or:I tried the following, but this also resulted in issue #2 I described above:
<script src="failing external CDN script" defer onerror="
var script = document.createElement('script');
script.async = false;
script.src = 'my own hosted version the script';
document.body.appendChild(script);
"></script>
Upvotes: 3
Views: 4395
Reputation: 1075597
I have these scripts that I want to download/execute asynchronously to prevent page render blocking
The simplest way to do that is just to put them at the end of the page, just before the closing </body>
tag. They don't render-block there, and you can do the fallback trivially:
<script src="jQuery CDN"></script>
<script>
if (typeof jQuery === "undefined") {
// ...add your local version instead...
}
</script>
You've said in a comment that PageSpeed will complain about script
tags just before </body>
if they don't have defer
. With respect, it doesn't (note that the link to code.jquery.com
is intentionally broken):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="favicon.png">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
Content here, turns green on click
</div>
</div>
</div>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<script src="https://code.jquery.com/jquery-2.2.4.mn.js"></script>
<script>
if (typeof jQuery === "undefined") {
document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"><\/script>');
}
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>
$(".col-md-12").on("click", function() {
$(this).css("color", "green");
});
</script>
</body>
</html>
That gets 100/100 from PageSpeed (if you host it properly — compression, etc.).
Now, if I had the CSS link
at the bottom, I'd also have inline CSS at the top to style the above-the-fold content. (But I probably wouldn't bother, I'd just have the link
at the top and accept the hit. I only put it at the bottom of that example because PageSpeed lumps together JS and CSS in its warning on the topic, and I wanted to demonstrate that script
just before </body>
doesn't trigger that error...)
But if you want to use defer
, you'd have to listen for the error
event as shown in your question. And of course, that means you have to hold off adding the scripts that are dependent on jQuery until you've had load
from the original script or its replacement, since you can't insert into the pipeline. E.g.:
<script src="failing external CDN script" defer onerror="
var script = document.createElement('script');
script.async = false;
script.onload = addDependencies;
script.src = 'my own hosted version the script';
document.body.appendChild(script);
" onload="addDependencies()"></script>
...where addDependencies
adds script
tags for the things depending on the script.
Upvotes: 3