问题
I have the following html code:
<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <title>Document</title> <script src=\"https://cdn.jsdelivr.net/blazy/1.8.2/blazy.min.js\" defer></script> <script src=\"https://code.jquery.com/jquery-2.1.4.min.js\" integrity=\"sha256-8WqyJLuWKRBVhxXIL1jBDD7SDxU936oZkCnxQbWwJVw=\" crossorigin=\"anonymous\" defer></script> <script src=\"https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.9.0/js/lightbox.min.js\" defer></script> <script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js\" integrity=\"sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa\" crossorigin=\"anonymous\" defer></script> <!-- 26 dec flexslider js --> <script src=\"https://cdnjs.cloudflare.com/ajax/libs/flexslider/2.6.3/jquery.flexslider.min.js\" defer></script> <script defer> (function($) { $(document).ready(function() { //do something with b-lazy plugin, lightbox plugin and then with flexslider }); })(jQuery); </script> </head> <body> </body> </html>
I get an error, saying jQuery is not defined. Now even if I remove defer from my inline JS code, it says jQuery is undefined. For some reason I have to keep the jQuery plugins in the head and keep my JS code inline. My question is:
Why doesn\'t inline Javascript code get deferred when
defer
attribute is present on it?Is there a way to imitate the defer behavior on my inline Javascript code? I can put that at the end of body tag if required.
回答1:
The scripts with the defer
attribute load in the order they are specified, but not before the document itself has been loaded. As defer
has no effect on script
tags unless they also have the src
attribute, the first script that gets executed is your inline script. So at that time jQuery is not loaded yet.
You can solve this in at least two ways:
Put your inline script in a
.js
file and reference it with asrc
attribute (in addition to thedefer
attribute which you already had there), orLet your inline script wait for the document and the deferred scripts to be loaded. The
DOMContentLoaded
event will fire when that has happened:<script> window.addEventListener('DOMContentLoaded', function() { (function($) { //do something with b-lazy plugin, lightbox plugin and then with flexslider })(jQuery); }); </script>
NB: Notice that in the latter case $(document).ready(function()
is not included any more, as that would wait for the same event (DOMContentLoaded
). You could still include it like you had in your original code, but then jQuery would just execute the callback immediately, which makes no practical difference.
回答2:
You can create a Base64 URL out of the script and put it into the src!
<script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw==" defer> </script>
I built a quick test to see it in action. You should see an alert with Hello world!
last if defer
is working:
<script defer> alert('Why no defer?!?'); </script> <!-- alert('Hello world!'); --> <script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw==" defer></script> <script> alert('Buh-bye world!'); </script>
Doing it manually is a little laborious so if you have the luxury of compiling your HTML in some way (Handlebars, Angular, etc.) then that helps a lot.
I'm currently using:
<script src="data:text/javascript;base64,{{base64 "alert('Hello world!');"}}" defer> </script>
回答3:
From MDN docs:
defer
This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firingDOMContentLoaded
. The defer attribute should only be used on external scripts.
This is called an IIFE (Immediately Invoked Function Expression) which gets executed before DOM is available. So, in that case jQuery
is undefined because it it not in the DOM.
回答4:
Defer/async script tags are not good enough
There is a common knowledge that you should use <script src=".." async defer>
(or set script.async = true
before assigning src
, when you do it from JS) and/or put your scripts at the very bottom of the page, so that as much as possible of the page gets loaded and rendered to the user, as fast as possible.
defer.js (note: I am the author of this script) is written in plain JavaScript, making lazy-loading other contents more fast and performant. You can defer any javascript files as well as inline script blocks efficiently.
If your page is just an HTML page enhanced with some JavaScript, then you're good with just <script async>
. It takes time for browser to parse and execute those scripts, and each UI change may reflow your layout, make your load speed more slow, no one likes staring at a blank white page; users are impatient and will leave quickly.
In various cases, using async
or defer
does not deliver faster page speed than defer.js does.
回答5:
defer loading with plain text Data URI - Chrome and FF
#noLib #vanillaJS
suggest not to use on Cross Browser PRODuction
until MS IE dies and MS Edge will adopt the Chromium open source ;)
the only way to defer script is external file or Data_URI (without using event DOMContentLoaded)
defer
spec script#attr-defer (MDN web docs): "This attribute must not be used if the src attribute is absent (i.e. for inline scripts), in this case it would have no effect.)"
Data_URI
spec Data_URI
with right type "text/javascript" there is no need to base64 at all... ;)
using plain text so you can use simple:
<script defer src="data:text/javascript, //do something with b-lazy plugin, lightbox plugin and then with flexslider lightbox.option({ resizeDuration: 200, wrapAround: true }) ">
yes, it's little bit weird hack, but <script type="module">
are deferred by default, there is no other option to mix following in exact order:
- module external files - deferred by default
- module inline scripts - deferred by default
- external files - optionally deferred
- inline scripts - only with this hack - as I know (without libraries/frameworks)
来源:https://stackoverflow.com/questions/41394983/how-to-defer-inline-javascript