5 ms·
About time. Just do as Google/Facebook do: var _script = document.createElement('script'); _script.src = '//blablabla.bla/bla.js'; /* _script.asy
by wanda 10y ago
About time. Just do as Google/Facebook do:
var _script = document.createElement('script');
_script.src = '//blablabla.bla/bla.js';
/* _script.async = '' */
(document.body || document.getElementsByTagName('body')[0])
.appendChild(_script);
You could append to the head instead (a la Google Analytics universal snippet) with the async attribute set to '' or 'async' or true or !0 or whatever you fancy.
Works for CSS too, though it'd be better to append your link element to the head despite what Google says.
Handy for asynchronously loading fonts without link rel="preload" which is poorly supported.
You could also do this with a single style element in the body containing your @font-face, but that's a crap idea if you want to use Google Fonts, for instance.
Obviously with CSS, it's worth including a regular link element in a noscript in the head.
- amelius 10y agoWhy not just use "eval" to load new symbols into Javascript's global namespace?
- gnode 10y agoNot exactly sure why you got downvoted. Perhaps people should actually say why they think eval is a bad idea. My guess is it's something to do with cross site scripting. I can see advantages to using XMLHttpRequest in conjuction with eval in some cases, such as being able to monitor script download progress with the onprogress event.
- yuhong 10y agoThe fun thing is that XMLHttpRequest is much better designed as an API. The poor design of the script element objects caused problems when HTML5 tried to standardize it: https://hsivonen.fi/script-execution/ https://hsivonen.fi/script-execution/
- drostie 10y agoYou shouldn't be downvoted by the eval-is-evil folks for this; it's a legitimate question that has nothing to do with the sins of eval[1], since you're giving the page 100% full control anyway. The answer is actually a bit common-sense after a moment: the difference between injecting a <script> and a eval() is that in the former case the code is in a file on some server, whereas in the latter case it's in a string. To hack up a solution with eval requires converting the file into a string, but to do that you need an XMLHttpRequest and to deal with origin policies and it rapidly explodes into a pointless mess because you're explicitly requesting that the loaded script have the power to interact with the context you're in, even if you're not using this power by boxing it into a context. (For example when you eval() in `function () { var secret = "this string is totally private!"; eval(code) }` that code has a power which no script tag has.) [1] The sins of eval include ugly metaprogramming where it's not needed like `eval("someObject." + property + ".otherThing")` rather than `someObject[property].otherThing`, and accidental remote code exploits where none was intended like `var json = eval(thing_i_dont_actually_trust)`: whoops, someone passed `(function() { ... }())` instead of JSON and it went straight through my parser.
- riskable 10y agoNo need for an XMLHttpRequest when you've got a websocket open to retrieve such scripts. In fact, loading your scripts via a websocket can be faster than loading them via parallel vanilla HTTP requests because the websocket won't have the overhead associated with GETs (no new TCP connections or headers to slow you down). Furthermore, if you cache said scripts in, say, IndexedDB you can just perform out-of-date checks via the websocket and only re-sync those scripts that have changed on the server resulting in even faster load times. This works and it's great. How do I know? I implemented such a solution in Gate One a few years ago: https://github.com/liftoff/GateOne https://github.com/liftoff/GateOne It also has the added benefit of making embedding a web application into another so much easier. Because you can just load a tiny little script on a page that makes just one connection to an external websocket and can load everything from that in an instant.
- wanda 10y agoeval is not necessarily evil in this case indeed. However, it'll force you to make concessions in your Content Security Policy and probably not very good for caching.
- riskable 10y agoIf you're using eval() you can cache scripts wherever you want! I personally prefer IndexedDB but there's nothing stopping you from using localStorage or a even a service worker (with a little bit of cache trickery). Also, if you use a websocket you can pretty much forget about the content security restrictions. Websockets have no origin restrictions so you can load whatever you want from anywhere.
- imtringued 10y agoI don't use eval so maybe I'm wrong but I guess it's easier to debug an error message when it contains both the filename and the line number.
- libertymcateer 10y agoWhat does the commented line accomplish? Edit: I am getting upvotes but no answer - it is a reasonable question. Why include it if it is commented out? Does it accomplish something, even though it is in comments? Edit: answered below!
- bluesmoon 10y agoScripts injected via JavaScript are implicitly async so you don't need to specify that (at least from Firefox 4.0 and Opera 15 onwards, all other browsers have always behaved this way).
- deleted 10y ago[deleted]
- josephclay 10y agoasync[1] changes how the scripts executes so that it doesn't block the browser. [1]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptE...
- wanda 10y agoI just meant to indicate that if nothing depends on the script, you can also set the async attribute to prevent even the slightest bit of blocking. Not really necessary if you append the script to the body, but it's good to do it if you append the script to the head.
- libertymcateer 10y agoHugely helpful - thanks!
- bluesmoon 10y agoOn modern browsers you don't even need this method. A simple <script src="//blablabla.bla/bla.js" async></script> will get you the same effect with the added advantage that the browser's lookahead parser can detect the script and start downloading it even before the DOM has been constructed. The problem with both methods, however, is that they will still block the onload event from firing until after the script has finished downloading. There are only two ways to get around that. The first is silly in how obvious it is... if you load a script after onload has happened, then it cannot block onload. This has the downside of not using parallelism. The second method is to load the script in an iframe whose onload event has already fired. It's a bit more involved, and absolutely requires the use of document.write inside the iframe. I've documented the method here, and it works on all browsers: http://www.lognormal.com/blog/2012/12/12/the-script-loader-pattern/ http://www.lognormal.com/blog/2012/12/12/the-script-loader-p...
- taeric 10y agoI'm not sure I understand your point. Isn't the goal of this technique to add somewhat dynamic scripts to the page? Specifically, the page includes "foo" but it basically just bootstraps "bar" and "baz." That is, if you already know the script you want to load, yes. Just load it. This is for the case where you are loading a script that will, in turn, decide what scripts need loading.
- acdha 10y agoI think that's confused because a lot of people read a blog post from a decade ago about a dynamic script loader and IE6, and have retained the mistaken belief that a script loader is necessary for performance. I regularly encounter websites which wait for a loader script to initialize before starting to request a static set of JS+CSS files, where a simple <script defer> or <script async> would have been both easier and considerably faster. (That's not to say that dynamic loading isn't a useful tool, only that a non-trivial number of web developers have fossilized impressions of the modern web performance environment)
- 10y ago
- kafkaesq 10y agoKudos for thinking that up. But it has to be said -- what an utterly demented programming environment. We're 20 years in now with JS/CSS/HTML, and still no light at the end of the tunnel.