6

Consider the following:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="script.js"></script>
    <script type="text/javascript">
        document.write('<script type="text/javascript" src="script2.js"></scr'+'ipt>');
        document.write('<script type="text/javascript" src="script3.js"></scr'+'ipt>');
        console.log(document.getElementsByTagName("script").length + " Scripts");
        console.log(document.head.innerHTML);
    </script>
</head>
<body>
</body>
</html>

What would you expect the console.log to contain? I hope you come to one of the two outcomes that I would expect: 4 Scripts and either the two existing or all four script tags shown in the head's innerHTML (document.write could as well write to the body, so one could expect the script tags to be injected as children of the body).

The thing is that, in Chrome and IE11, the first script tag added via document.write is shown in the head's innerHTML, but the second isn't, and the DOM query result is 3 Scripts.

Could anyone please elaborate?

Alexander
  • 18,932
  • 15
  • 54
  • 138
  • 1
    Why the close request? It is a completely valid question and even has 5 upvotes – mplungjan Apr 10 '15 at 09:41
  • @mplungjan upvotes don't always indicate appropriateness of a question (didn't vote) – John Dvorak Apr 10 '15 at 09:44
  • I set script.js to x=1, script2.js to y=1 and script3.js to z=1 and I get y is undefined – mplungjan Apr 10 '15 at 09:46
  • @mplungjan Me too. `script2.js` is in innerHTML, `y` is undefined, but a `console.log` inside `script2.js` is printed into console! – Alexander Apr 10 '15 at 09:51
  • 1
    FWIW I got curious and posted a link in [chat](http://chat.stackoverflow.com/transcript/message/22603409#22603409). Seems safe to assume that the behaviour when doing that is undefined. – ivarni Apr 10 '15 at 09:54
  • 1
    Use `script.addEventListener('load', function () { console.log('Now the script should be loaded and ready to go') })`. – powerbuoy Apr 10 '15 at 10:36

4 Answers4

2

Giving it a chance to render works in Chrome:

In the script I have script.js: x=1, script2.js: y=1 and script3.js:z=1

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="script.js"></script>
    <script type="text/javascript">
        console.log("after 1",x);
        document.write('<script type="text/javascript" src="script2.js"><\/script>');
        document.write('<script type="text/javascript" src="script3.js"><\/script>');
        setTimeout(function() {
          console.log(document.getElementsByTagName("script").length + " Scripts");
          console.log(document.head.innerHTML);
          console.log("after 3",x,y,z)
        },100); // the milliseconds MAY need to be higher over the net
    </script>
</head>
<body>
</body>

Result:

4 Scripts

<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="script.js"></script>
<script type="text/javascript">
    document.write('<script type="text/javascript" src="script2.js"><\/script>');
    document.write('<script type="text/javascript" src="script3.js"><\/script>');
    setTimeout(function() {
      console.log(document.getElementsByTagName("script").length + " Scripts");
      console.log(document.head.innerHTML);
      console.log("after 3",x,y,z)
      },10)
</script><script type="text/javascript" src="script2.js"></script><script type="text/javascript" src="script3.js"></script>

after 3 1 1 1
mplungjan
  • 134,906
  • 25
  • 152
  • 209
1

Using document.write() is the old and bad way of doing things, you should be using the DOM :

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="script.js"></script>
    <script type="text/javascript">
        // Child 1
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'script2.js';
        document.getElementsByTagName('head')[0].appendChild(script);
        //Child 2
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'script3.js';
        document.getElementsByTagName('head')[0].appendChild(script);

        console.log(document.getElementsByTagName("script").length + " Scripts");
        console.log(document.head.innerHTML);
    </script>
</head>
<body>
</body>
</html>

It returns 4 Scripts

Community
  • 1
  • 1
Hamed Kamrava
  • 10,533
  • 32
  • 78
  • 117
1

One possible explanation of the 3 count is that the scripts added via document.write are executed synchronously. So:

  1. Script engine executes the script block that contains document.write

    • Script engine adds the first script to DOM.
    • Script engine does not add the second script to DOM -- it must wait for the first script to download and execute. After all, it is possible for the first script to do a document.write("<!--");
    • Script engine returns the count 3
  2. Script engine downloads the first script and executes it.

  3. Script engine adds the second script to the DOM, downloads it and executes it.

Keep in mind that JavaScript is single threaded. This prevents the execution of script tags generated by document.write until the script that generates script tags is finished.

Salman A
  • 229,425
  • 77
  • 398
  • 489
0

Is this because you're writing a script tag inside another script tag and ultimately creating invalid markup (i could be wrong, but my theory matches what you are seeing)

1---> <script type="text/javascript" src="script.js"></script>

2---> <script type="text/javascript">
          <script type="text/javascript" src="script2.js">
       </script>

3---> <script type="text/javascript" src="script3.js">
          </script>
      </script>

maybe?!

UPDATE

After a little playing I agree with @mplungjan, it's a timing issue. This works as expected. Scripts count is now obvs 5

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="script.js"></script>
    <script type="text/javascript">
        document.write('<script type="text/javascript" src="script2.js"></scr'+'ipt>');
        document.write('<script type="text/javascript" src="script3.js"></scr'+'ipt>');
    </script>
    <script type="text/javascript">
        console.log(document.getElementsByTagName("script").length + " Scripts");
        console.log(document.head.innerHTML);
    </script>
</head>
<body>
</body>
</html>
matt_lethargic
  • 2,506
  • 1
  • 15
  • 29
  • The escaping of the end tag USED to make it valid – mplungjan Apr 10 '15 at 09:45
  • @mplungjan doesn't it still? – John Dvorak Apr 10 '15 at 09:47
  • It still makes it valid. But see my answer for the reason for the inconsitency (PS: I did not downvote) – mplungjan Apr 10 '15 at 09:57
  • 1
    If that was true, then almost **any** use of `document.write()` would generate invalid code. The entire script element is parsed before the script is executed. `document.write()` will put content immediately after the script element. (Or if you mean that the string ` – Quentin Apr 10 '15 at 09:57
  • Could the down voter please explain why. And yes @Quentin, I was wrong in my first answer. – matt_lethargic Apr 10 '15 at 10:00