84

I am trying to load a certain script after page load executes, something like this:

function downloadJSAtOnload(){
            var element = document.createElement("script");
            element.src = "scriptSrc";
            document.body.appendChild(element);
        }

         if (window.addEventListener)
                  window.addEventListener("load", downloadJSAtOnload, false);
            else if (window.attachEvent)
                  window.attachEvent("onload", downloadJSAtOnload);
            else window.onload = downloadJSAtOnload;

And while this script seems to execute and download 'scriptSrc', and append it right before the end of the body tag, it yields the following message (not an error) in the console (chrome)

Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.

What does this even mean? And am I supposed to do something differently? Even though I get the expected behavior?

Parijat Kalia
  • 4,929
  • 10
  • 50
  • 77
  • 2
    It means the script you are loading executes `document.write`, which is not supported when you add it to the page via a ` – univerio Jun 19 '14 at 02:28
  • 1
    got it, but I can't change that script. Call it bureaucracy. Can I get the script to load synchronously? No right, cause it happens after page load. – Parijat Kalia Jun 19 '14 at 02:38
  • Yes, you can load the script contents synchronously with AJAX, then `eval` it, though I'm not 100% sure whether that would work. – univerio Jun 19 '14 at 02:49
  • 2
    recently found a fantastic library on github that gets around this "restriction" nicely: https://github.com/krux/postscribe – Randy L Sep 16 '15 at 23:52
  • @univerio I am also facing same issue. `document.write('\x3Cscript type="text/javascript" src="http://ads.appnexus.com/ttj?id=xyzpqr&cb=${CACHEBUSTER}">\x3C/script>');` this is working perfectly but when I create a script using createElement('script') I am facing same issue. Can you suggest something here? – RockStar May 18 '16 at 11:13
  • @univerio I'm fine if script run Synchronously, I set async false property too but no luck. – RockStar May 18 '16 at 11:15
  • @RockStar Don't use `document.write`. If you can't change the script that uses it, use postscribe as suggested above. – univerio May 18 '16 at 18:56
  • @univerio I used postscribe looks like somehow it sort out my problem. But this tag we send to client so every JavaScript tags we needed to include this lib from cdn which is again synchronous. So what is best way to use that lib with our JavaScript tag which we give to client. If one page having 5 tags and each every loading postscribe from cdn synchronously not looks good. – RockStar May 19 '16 at 06:09
  • Google ads is causing this issue for me – PandaWood Jul 05 '16 at 11:08
  • Same issue here with async set to false. I still get the same error. Any ideas if this is the same problem with document.write? Website url where this issue happens: https://teamhood.com/ – VidasV Jan 25 '22 at 11:11
  • See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/dom/document.cc;l=4217-4226;drc=07b6b88e38cb2e2b8dfaccf25b27fefd66766349 and the spec https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#ignore-destructive-writes-counter: _Document objects have an ignore-destructive-writes counter, which is used in conjunction with the processing of script elements to prevent external scripts from being able to use document.write() to blow away the document by implicitly calling document.open(). Initially, the counter must be set to zero._ – cachius May 22 '23 at 20:30

4 Answers4

101

An asynchronously loaded script is likely going to run AFTER the document has been fully parsed and closed. Thus, you can't use document.write() from such a script (well technically you can, but it won't do what you want).

You will need to replace any document.write() statements in that script with explicit DOM manipulations by creating the DOM elements and then inserting them into a particular parent with .appendChild() or .insertBefore() or setting .innerHTML or some mechanism for direct DOM manipulation like that.

For example, instead of this type of code in an inline script:

<div id="container">
<script>
document.write('<span style="color:red;">Hello</span>');
</script>
</div>

You would use this to replace the inline script above in a dynamically loaded script:

var container = document.getElementById("container");
var content = document.createElement("span");
content.style.color = "red";
content.innerHTML = "Hello";
container.appendChild(content);

Or, if there was no other content in the container that you needed to just append to, you could simply do this:

var container = document.getElementById("container");
container.innerHTML = '<span style="color:red;">Hello</span>';
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • But am I not doing something similar when I state "document.body.appendChild(element)" ? – Parijat Kalia Jun 19 '14 at 02:39
  • @jfriend, Re "*but it won't do what you want*", What if it does? **How do I enable it if I want it?** (This used to work on older browsers.) – Pacerier Sep 28 '17 at 12:42
  • 2
    @Pacerier - Using `document.write()` after the document has finished loading causes the current document to be cleared and then the write goes into a new blank document. If you want that behavior, then just clear the current DOM before inserting your content. If you have a more detailed question than that, then please ask a new question, show the code you have and describe the desired outcome. – jfriend00 Sep 28 '17 at 20:41
23

A bit late to the party, but Krux has created a script for this, called Postscribe. We were able to use this to get past this issue.

Randy L
  • 14,384
  • 14
  • 44
  • 73
  • 1
    what is best way to pass this lib with our JavaScript lib? Situation is that we give JS tags to client which runs on there website. – RockStar May 19 '16 at 06:12
  • I'm not 100% sure what you're asking here @RockStar but any normal means of including JS should work. You could pull up https://cdnjs.com/libraries/postscribe and copy/paste the code into an existing JS file, or you could just add a – Randy L May 19 '16 at 15:10
  • 1
    We gave JavaScript CDN link to website owner to place advertise using this tag. We can't able ask every owner to place this postscribe.js so we need to give it with our JS. – RockStar May 20 '16 at 14:09
  • postscribe doesn't seem to do anything at all unless you explicitly use the `postscribe` function - very disappointing – Ray Nicholus Apr 12 '18 at 14:52
  • @RockStar What is the solution which you got for your problem. Even I have the same problem – Thilak Raj Jul 28 '21 at 15:40
3

In case this is useful to anyone I had this same issue. I was bringing in a footer into a web page via jQuery. Inside that footer were some Google scripts for ads and retargeting. I had to move those scripts from the footer and place them directly in the page and that eliminated the notice.

mediaguru
  • 1,807
  • 18
  • 24
  • 1
    I had the same issue, but the script was inside a bundle created by a Tag Manager. The issue was caused by the `async` attribute, and the only solution was to remove that attribute, as I am not allowed to move scripts from our Tag Manager. – Giorgio Tempesta Jun 19 '18 at 13:50
2

You can also call

document.open() before document.write()

call

document.close() 

when you're done.

It may not be best practice for a real webpage but for testing etc.. can be used.

C B
  • 12,482
  • 5
  • 36
  • 48
  • Really useful for multiple writes is `document.writeln` with `document.body.style.whiteSpace = "pre";` at the end – cachius May 22 '23 at 20:15