28

I have often used, and seen recommended, dom-access structures like this for adding content to pages dynamically:

loader = document.createElement('script');
loader.src = "myurl.js";
document.getElementsByTagName('head')[0].appendChild(loader);

Now, by chance, I find that this works in Google chrome:

document.head.appendChild(loader);

A little more investigation, and I find that this works, apparently cross-browser:

document.body.appendChild(loader);

So my primary question is: are there any reasons why I shouldn't attach elements to the BODY like this?

Also, do you think document.head will become more widely supported?

graphicdivine
  • 10,539
  • 7
  • 30
  • 58
  • 9
    Check this deep analysis made by Stoyan Stefanov: [The ridiculous case of adding a script element](http://www.jspatterns.com/the-ridiculous-case-of-adding-a-script-element/) – Christian C. Salvadó Sep 26 '11 at 17:26
  • See [my answer](https://stackoverflow.com/a/12113657/1037948) on a similar question for a more thorough comparison of the options. (i.e. section/script.appendChild vs. section/script.insertBefore) – drzaus Jun 03 '13 at 14:17

4 Answers4

14

I can’t see any reason why it would matter in practice whether you insert your <script> elements into the <head> or the <body> element. In theory, I guess it’s nice to have the runtime DOM resemble the would-be static one.

As for document.head, it’s part of HTML5 and apparently already implemented in the latest builds of all major browsers (see http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-head).

Daniel Brockman
  • 16,714
  • 3
  • 25
  • 40
  • What about adding a stylesheet as a plugin where you want people to be able to overwrite certain styles from their CSS that's positioned in the head, without having to add `!important` to everything – Richard Sep 26 '14 at 07:32
8

document.body is part of the DOM specification, I don't see any point why not to use it. But be aware of this:

In documents with contents, returns the element, and in frameset documents, this returns the outermost element.

(from https://developer.mozilla.org/en/DOM/document.body)

document.head currently isn't defined in any DOM specification (apparently I was wrong on that, see Daniel's answer), so you should generally avoid using it.

Niko
  • 25,682
  • 7
  • 85
  • 108
2

I tried implementing this code and ran into a bit of trouble, so wanted to share my experience.

First I tried this:

<script>
loader = document.createElement('script');
loader.src = "script.js";
document.getElementsByTagName('head')[0].appendChild(loader);
</script>

And in the script.js file I had code such as the following:

// This javascript tries to include a special css doc in the html header if windowsize is smaller than my normal conditions.  
winWidth = document.etElementById() ? document.body.clientWidth : window.innerWidth; 
if(winWidth <= 1280) { document.write('<link rel="stylesheet" type="text/css" href="style/screen_less_1280x.css">'); } 

The problem is, when I did all of this, the code wouldn't work. Whereas it did work once I replaced the script loader with simply this:

<script src="script.js"></script>

That works for me, so problem solved for now, but I would like to understand the difference between those two approaches. Why did one work and not the other?

What's more is that in script.js I also have code such as:

function OpenVideo(VideoSrcURL) {
window.location.href="#OpenModal";
document.getElementsByTagName('video')[0].src=VideoSrcURL;
document.getElementsByTagName('video')[0].play();}

And that code does work fine regardless of which way I source the script in my html file.

So my window resizing script doesn't work, but the video stuff does. Therefore I'm wondering if the difference in behavior has to do with "document" object...? Maybe "document" is referencing the script.js file instead of the html file.

I don't know. Thought I should share this issue in case it applies to anyone else.

Cheers.

  • 1
    I know this is really late, but you were really close. el = document.createElement('script'); el.src = 'script.js'; var parent = document.getElementsByTagName('head').item(0) || document.documentElement; parent.appendChild(el); – Sparatan117 Dec 29 '15 at 06:32
  • Why did you make your question as an answer? -1 so you will do it –  Jun 11 '19 at 17:44
1

The answers given as far as now focus two different aspects and are both very useful.

If portability is a requirement for you, in documents not under your ownership where you can't control the DOM coherence, it may be useful to check for the existence of the element you have to append the script to; this way, it will works also when the HEAD section has not been explicitly created:

var script = document.createElement('script');
var parent = document.getElementsByTagName('head').item(0) || document.documentElement;
parent.appendChild(script);

Apart of CORS policy and other easily detectable logic errors, I don't see cases in which this DOM manipulation task should fail, so checking for document.body as a fallback is not necessary by my point of view.

As other users outlined too, document.head was not widely supported yet, at the time the question was posted, as it is a HTML5 spec, while document.body is, that's why you saw the latter working on all the browsers.

So you could get the HEAD with:

document.head || document.getElementsByTagName('head').item(0)

but I don't see it very useful, as the latter won't ever be wrong and is a readable and standard DOM query, unless you find out that document.head is faster from a performance perspective.

Another possible advantage I can see about this form is a code transition from older JavaScript to a more modern HTML5 compliant code, brief and more readable.

If compatibility is a must, you could use tricks adopted by libraries for which portability is mandatory (e.g. solutions from Google, Yandex and others):

var parent = document.getElementsByTagName('head')[0] || document.documentElement.firstChild;
parent.appendChild(script);

This way, in the improbable case the HEAD element does not exist while the BODY (or another element) does, you are sure to append the script to it.

I also add a useful bit: the HTTP request of the script source is sent from the browser when the src attribute is set and it is appended to the DOM. No matter in which order these two conditions are met, the last of these two events causes the HTTP request to be dispatched.

yodabar
  • 4,262
  • 1
  • 28
  • 36
  • Why not check for `document.head` at first? –  Jun 11 '19 at 17:27
  • Confirmed to have true on this: `alert(document.head == document.getElementsByTagName('head').item(0))` –  Jun 11 '19 at 17:40
  • Explain the code and you have the best cross-browser answer! Take little from this: https://stackoverflow.com/questions/12113412/dynamically-inject-javascript-file-why-do-most-examples-append-to-head/12113657#12113657 –  Jun 11 '19 at 17:52