可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I noticed in the <head>
of my site (for work), there are a lot of <link rel="stylesheet" type="text/css" href="" />
and <script type="text/javascript" src="">
tags. There are even more JavaScript/CSS files that are only loaded for specific pages (we're using CodeIgniter, and the file paths are passed to the header view).
I was considering using a conditional/asynchronous loader (eg. yepnope.js, head.js, etc.), but I noticed a small problem with doing this.
In our views, there is inline JavaScript, some uses $(function(){})
some uses $(document).ready(function(){})
, and some just has code (using jQuery) that's not in a ready
block.
Without editing EVERY view file to wrap its code in a function and calling that when the JS files are loaded, is there a way to delay the inline code until the JavaScript is asynchronously loaded?
回答1:
You can actually lazyload inline javascript:
1- Change the type parameter in the inline script to: text/delayscript
FROM
<!– Inline Script –>
<script type="text/javascript" language="javaScript">
/* Code */
</script>
To
<!– Inline Script –>
<script type="text/delayscript">
/* Code */
</script>
Giving the script tag a custom Mime type text/delayscript forces the browser to ignore its content (Please note that leaving it out entirely will default to text/javascript).
2- Lazy load all inline scripts
Once heads.js (Or an other framework you might be using) confirms that it lazy loaded all your external JS, you can then grab the content of all your custom script tags and inject them in the page:
<script>
head.ready(function() {
var
_head = document.getElementsByTagName("head")[0],
_script = document.createElement('script'),
_scripts = document.getElementsByTagName("script"),
_txt = "text/delayscript",
_contents = []
;
for(var i=0,l=_scripts.length;i<l;i++){
var _type = _scripts[i].getAttribute("type");
if(_type && _type.toLowerCase() ==_txt)
_contents.push(_scripts[i].innerHTML)
}
_script.type = 'text/javascript';
_script.innerHTML = _contents.join(" ");
_head.appendChild(_script);
});
To be even more graceful, you can actually keep the inline scripts in their original hierarchy in the DOM tree instead of jamming all their content in one script, as I have suggested above, by replacing the marked inline script tag by a new one that has mime type text/javascript:
head.ready(function() {
var
_scripts = document.getElementsByTagName("script"),
_doc = document,
_txt = "text/delayscript"
;
for(var i=0,l=_scripts.length;i<l;i++){
var _type = _scripts[i].getAttribute("type");
if(_type && _type.toLowerCase() ==_txt)
_scripts[i].parentNode.replaceChild((function(sB){
var _s = _doc.createElement('script');
_s.type = 'text/javascript';
_s.innerHTML = sB.innerHTML;
return _s;
})(_scripts[i]), _scripts[i]);
}
});
回答2:
You have to consider moving inline code "outside" and include it with
<script defer="defer" type="text/javascript" src="">
回答3:
First of all I would recommend you to analyse the loading of the scripts on the client side very carefully and not only for the first JavaScript loading, but on the loading of the same JavaScript files at the second time or its loading on another page. If the ETag are correctly set on the scripts by the web server or if you use other caching options of HTTP (see the caching tutorial for details) for the JavaScripts files than no loading of the files itself will take place and only the cache revalidation will be done. So it can be that the problem which you describes is not so important like it looks like.
If you do decide to load some scripts dynamically you can use jQuery.getScript and place all dependent code inside of the success
callback. If you need to load one or two JavaScript files the way will work very good, but if you need to load a list of JavaScript files with more complex dependency the implementation could be not so easy. In the case you can use document.writeln
inside of <head>
. The usage of the method inside of <head>
has almost no disadvantages (see here for details).
回答4:
HTML5 introduced a new async
parameter for scripts having a defined src
.
You can add it directly on any <script>
element :
<script src='/js/script.js' async></script>
BUT : keep in mind that it will not work on inline scripts!
And if your some of your pages mix either external and inline scripts, if you load the external asynchronously, it means that the inline scripts will actually be executed before the asynchronous ones... Which can have unwanted effects.
Set this Boolean attribute to indicate that the browser should, if possible, execute the script asynchronously. It has no effect on inline scripts (i.e., scripts that don't have the src attribute).
For instance, if you have the following configuration :
<script src='/js/jquery.min.js' async></script>
<script>
// --> jQuery won't be loaded when this script will be executed!
// This will throw an error.
$(function () {
$('#element').doSomething();
});
</script>
回答5:
Depending on your setup, you might want to look into the service from www.cloudflare.com. Their current beta functionality called rocket loader, does exactly that, including the inline scripts.
It's a free service. try it out ;)
Plus you get a free caching proxy on top :)
回答6:
Instead of inline scripts, create inline functions. Then at the end of your javascript file, call the function if it exists.
function inline_script() {.. code ..}
Inside your concatenated jquery+etc async javascript file:
if (typeof(inline_script) == 'function') inline_script()
You'll have to do some shuffling to make sure you only have one inline_script per page, or use some kind of caching to put them together if you call it more than once.
It's still a fun problem in the current age even though this question is old, async scripts = pretty awesome
回答7:
Have a look at using headjs. It is a nice lightweight library that will do this all for you.