back

by masswerk·10y ago·view on hn ↗
From the documentation: "You shouldn't include it in the <head> of your web applications. It's bad practice to place scripts in the <head>, and as such dragula makes no effort to support this use case."

Serious question: With nearly 20 years in JS development, the concept of placing scripts not in the head section as being desirable is new to me. (Just saying, it used to be the other way round. Common resources, like scripts were, aside from the title, the main reason, the head tag was introduced at all.) -- Is there a source to this notion of script embedding, or can anyone clarify? (What would be the winning propositions of not using parallel loading slots?)

4 comments
If you have javascript in the head loading synchronously the page can't even begin to render until the javascript has loaded. Placing script tags at the bottom of the body allows the page to begin rendering while scripts are loading.
There's this facility called slots, at least this is, how it used to be. A resource to be loaded is assigned a slot as available (generally 8 or so) and the browser advances over resource tags. If a resource in the head section is going to be interpreted and is ready, the resource is processed, the slot is freed and available for further jobs. (This is partly, why only hard coded resources are of relevance for the document.DOMContentLoaded event.) There's no connection between single threaded execution and parallel slots.

(Edit: So this may be true for scripts that are requiring further resources, but there's no need for this with scripts that are completely self-contained, like dragula claims to be. Here, this might rather slow down the completion of the page.)

> There's no connection between single threaded execution and parallel slots

It has more to do w/ the DOM rendering. The rendering engine is required to wait for a javascript resource to download because it can't know in advance that the js file won't call `document.write`

`document.write` has a nasty property that it calls `document.open()` if the document is not open for writing (which clears the document). Before the DOMContentReady event, the document is open for writing (this is why, among other things, console.log(document.body) yields null and `document.write("<title>foo</title>") sets document.title` when called from <head>)

If parsing of the document was allowed to skip a js file until it downloaded, and if the script has a `document.write` call, then running the script upon download completion (which would typically happen much later than DOMContentReady fired) would trigger the implicit document.open() call, thus yielding different (and catastrophic) semantics.

What you're describing is the now ancient document stream model (document.open – document.write – document.close; calling document.open() after document.close implies document.open() and a loss of the content by an implied call to document.clear()). But I honestly hope, document.write() isn't used today (quite like "eval"), and everyone uses document.createElement(), etc.

As for the rendering engine having to wait for the resource as it hits it, this is exactly why you want it to be ready then, as opposed to just starting to download it and wait. (Even, if it is the very last tag, it will defer completion. And if every library were claiming to be the last one ...)

> If parsing of the document was allowed to skip a js file until it downloaded (…) – This is, what the "defer" and the "async" attributes are for. (And, again, you don't want to call document.open(), be it explicitly or implied, because you would lose any content there is, since it implies "document.clear()". This is Netscape Navigator 2.0 stuff, 1996, really.)

P.S.: What you're heading for, were mere work-arounds in times, when the document-object would have been created only as the parser hit the <body>–tag (up to and including Netscape 3) and when enumerated properties became available only after the body was opened (Netscape 4, etc). But even then, you would apologize in a comment, if such means were required. (E.g., while addressing Netscape 4's document.layers object in 1997.) Now, that there are so many means to do this appropriately, I see no reason at all of banning common resources from the head section and spreading them over the document.body.

If browsers decided to remove document.write and friends, then they would obtain a guarantee that running a script after the recursive DOM assembly routine yields the same result as running the script synchronously within it (regardless of what the script might potentially do), and they would be able to optimize the pipeline to allow you to put scripts in head without triggering a performance problem.

As far as usage in the wild goes, I've seen document-stream-related functions in rich text editors and comet (old school "websocket polyfills") codebases, but that was many years ago. I can't say for certain why browsers still support those functions.

I would say the best practise for modern browsers is to place this in the <head>:

    <script async src="script.js"></script>
Script tags at the bottom of body has become the accepted standard for faster page loads. I'd think the library would have to be doing some strange things to not work if placed in the head, though.
> I'd think the library would have to be doing some strange things to not work if placed in the head, though.

Not really. All it does is `var body = document.body` in an IIFE. In <head>, that yields undefined.

Why not use document.DOMContentLoaded ?
Embedding scripts in <head> is an established worst practice. It blocks rendering for no good reason. Sum that with the fact that dragula is a drag&drop library and as such you don't need it in the slightest to display your content, and it becomes obvious why I don't support <head> embedding.
Wouldn't a "defer" attribute and waiting for document.DOMContentLoaded be the appropriate way to do this? This would also document the reasons for this and would allow for a decent organization of the document (what the head section is good for). – To me, this is rather late-1990s style, before there were such things as defer and sophisticated DOM events.
That's interesting—I started learning JS last year and I was told to never put it in `<head>` but instead before the end of `<body>`.
Very interesting. I suppose, in fact this would be rather slowing down the page in most cases, if applied as a common rule. That is, provided you were using self-contained inline-resources only. Using any kind of include/require mechanism would change the picture in favor of a touch-as-late-as-possible strategy.