81

In tutorials I've learnt to use document.write. Now I understand that by many this is frowned upon. I've tried print(), but then it literally sends it to the printer.

So what are alternatives I should use, and why shouldn't I use document.write? Both w3schools and MDN use document.write.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
DarkLightA
  • 14,980
  • 18
  • 49
  • 57
  • 3
    `print()` resolves to `window.print()`. – alex Aug 29 '11 at 05:58
  • possible duplicate of [Replacing document.write()s in an xhtml+xml page](http://stackoverflow.com/questions/3614288/replacing-document-writes-in-an-xhtmlxml-page) – alk Feb 27 '14 at 17:56
  • 2
    JavaScript's `print()` is not the same as PHP's `print()`. JS literally sends the page to the printer, and PHP simply outputs code. – Ian Hazzard May 21 '15 at 15:02

12 Answers12

97

The reason that your HTML is replaced is because of an evil JavaScript function: document.write().

It is most definitely "bad form." It only works with webpages if you use it on the page load; and if you use it during runtime, it will replace your entire document with the input. And if you're applying it as strict XHTML structure it's not even valid code.


the problem:

document.write writes to the document stream. Calling document.write on a closed (or loaded) document automatically calls document.open which will clear the document.

-- quote from the MDN

document.write() has two henchmen, document.open(), and document.close(). When the HTML document is loading, the document is "open". When the document has finished loading, the document has "closed". Using document.write() at this point will erase your entire (closed) HTML document and replace it with a new (open) document. This means your webpage has erased itself and started writing a new page - from scratch.

I believe document.write() causes the browser to have a performance decrease as well (correct me if I am wrong).


an example:

This example writes output to the HTML document after the page has loaded. Watch document.write()'s evil powers clear the entire document when you press the "exterminate" button:

I am an ordinary HTML page.  I am innocent, and purely for informational purposes. Please do not <input type="button" onclick="document.write('This HTML page has been succesfully exterminated.')" value="exterminate"/>
me!

the alternatives:

  • .innerHTML This is a wonderful alternative, but this attribute has to be attached to the element where you want to put the text.

Example: document.getElementById('output1').innerHTML = 'Some text!';

  • .createTextNode() is the alternative recommended by the W3C.

Example: var para = document.createElement('p'); para.appendChild(document.createTextNode('Hello, '));

NOTE: This is known to have some performance decreases (slower than .innerHTML). I recommend using .innerHTML instead.


the example with the .innerHTML alternative:

I am an ordinary HTML page. 
I am innocent, and purely for informational purposes. 
Please do not 
<input type="button" onclick="document.getElementById('output1').innerHTML = 'There was an error exterminating this page. Please replace <code>.innerHTML</code> with <code>document.write()</code> to complete extermination.';" value="exterminate"/>
 me!
<p id="output1"></p>
Ian Hazzard
  • 7,661
  • 7
  • 34
  • 60
  • 1
    So the evil only starts after the document is closed? Besides being unfashionable, are there any downsides to using document.write before that? Is there any practical uncertainty about _when_ the document is closed? – Bob Stein May 29 '17 at 13:30
  • @BobStein-VisiBone, yes, `document.write()` can be called during the page load, while the browser is parsing the page. Once the page has parsed/loaded, calling this function will also call `document.open()`, which wipes the page and starts from scratch. I can't think of any reason not to use the function during page load, except for consistency with the way you add content to a page any other time. And the document is closed as soon as the browser has finished parsing the document. – Ian Hazzard May 30 '17 at 14:19
  • `document.write()` still seems to be the best solution to [cdn local fallback](https://stackoverflow.com/a/9400432/673991). I haven't tried getElementById/innerHTML yet, but a createElement/insertBefore solution turns out to be [nonsynchronous](https://stackoverflow.com/a/4825258/673991). – Bob Stein May 30 '17 at 15:51
  • 2
    @Godisgood, There's no way document.write has performance problems because its the exact same function used by browsers as they load the initial HTTP response. – Pacerier Oct 16 '17 at 15:02
  • @Pacerier Yes, but I meant it's slower to use `document.write()` after the pages has loaded because it has to clear the document and then reopen it every time the function is called. `.innerHTML` does none of that so I'm pretty sure I read that it is faster after all. I will look for a source to back that up. I believe `document.write()` is fast if you use it while the page is already open. – Ian Hazzard Nov 27 '17 at 04:07
30

Here is code that should replace document.write in-place:

document.write=function(s){
    var scripts = document.getElementsByTagName('script');
    var lastScript = scripts[scripts.length-1];
    lastScript.insertAdjacentHTML("beforebegin", s);
}
  • nice tip. Is it crossbrowser? I've tested it on Chrome and works. – Pons May 27 '14 at 13:31
  • 2
    Won't this not work if there are no scripts on the page? – Noyo Jul 10 '14 at 09:21
  • 1
    Well, there would be at least on the page (where ever you are putting that function), but what if the only script is located in the ? The key thing he mentions is the insertAdjacentHTML function. – Lonnie Best Aug 14 '14 at 17:38
  • Strange, I didn't have emails that someone was commenting until today LOL. @Pons - it should be cross browser, I don't know about IE though. – Adrian Kalbarczyk Aug 16 '14 at 09:24
  • 1
    @Noyo - usually you're using document.write inside – Adrian Kalbarczyk Aug 16 '14 at 09:27
  • Or more simply: `document.write = function (str){ document.body.insertAdjacentHTML("beforeend", str); }` – skibulk Dec 07 '14 at 16:28
  • @skibulk : While that can work for some cases, it doesn't have the benefit of working for drop-in amidst other HTML (e.g., for advertisements in the middle of a page). – Brett Zamir Nov 27 '17 at 13:13
  • Shouldn't the string be placed `'afterend'`, as opposed to `'beforebegin'` to match native `documet.write`? Not that it should matter, though. – Gust van de Wal Oct 02 '20 at 10:10
  • Okay I'm taking that last comment back. Looking at [Sorin's answer](https://stackoverflow.com/a/38906655/3791358)'s first revision, I realised that the order in which stuff is added to the DOM is probably more important than where it is relative to the `script` tag. – Gust van de Wal Oct 02 '20 at 10:21
19

You can combine insertAdjacentHTML method and document.currentScript property.

The insertAdjacentHTML() method of the Element interface parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position:

  • 'beforebegin': Before the element itself.
  • 'afterbegin': Just inside the element, before its first child.
  • 'beforeend': Just inside the element, after its last child.
  • 'afterend': After the element itself.

The document.currentScript property returns the <script> element whose script is currently being processed. Best position will be beforebegin — new HTML will be inserted before <script> itself. To match document.write's native behavior, one would position the text afterend, but then the nodes from consecutive calls to the function aren't placed in the same order as you called them (like document.write does), but in reverse. The order in which your HTML appears is probably more important than where they're place relative to the <script> tag, hence the use of beforebegin.

document.currentScript.insertAdjacentHTML(
  'beforebegin', 
  'This is a document.write alternative'
)
Gust van de Wal
  • 5,211
  • 1
  • 24
  • 48
Sorin
  • 221
  • 2
  • 4
  • 1
    Although this may answer the question, please provide any explanation you can to make sure people understand what is happening here. – Randy Aug 12 '16 at 09:32
  • Thanks to this comment here, my issue is resolved I have explained what was the issue and what worked for me here: https://stackoverflow.com/a/64610187/7266835 – K. Shaikh Oct 30 '20 at 14:35
14

As a recommended alternative to document.write you could use DOM manipulation to directly query and add node elements to the DOM.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 12
    This would be a better answer if it addressed the question of *why* document.write is problematic, as [this answer does](http://stackoverflow.com/a/27531116/19068). – Quentin Sep 12 '16 at 15:07
12

Just dropping a note here to say that, although using document.write is highly frowned upon due to performance concerns (synchronous DOM injection and evaluation), there is also no actual 1:1 alternative if you are using document.write to inject script tags on demand.

There are a lot of great ways to avoid having to do this (e.g. script loaders like RequireJS that manage your dependency chains) but they are more invasive and so are best used throughout the site/application.

James M. Greene
  • 1,251
  • 1
  • 16
  • 23
8

I fail to see the problem with document.write. If you are using it before the onload event fires, as you presumably are, to build elements from structured data for instance, it is the appropriate tool to use. There is no performance advantage to using insertAdjacentHTML or explicitly adding nodes to the DOM after it has been built. I just tested it three different ways with an old script I once used to schedule incoming modem calls for a 24/7 service on a bank of 4 modems.

By the time it is finished this script creates over 3000 DOM nodes, mostly table cells. On a 7 year old PC running Firefox on Vista, this little exercise takes less than 2 seconds using document.write from a local 12kb source file and three 1px GIFs which are re-used about 2000 times. The page just pops into existence fully formed, ready to handle events.

Using insertAdjacentHTML is not a direct substitute as the browser closes tags which the script requires remain open, and takes twice as long to ultimately create a mangled page. Writing all the pieces to a string and then passing it to insertAdjacentHTML takes even longer, but at least you get the page as designed. Other options (like manually re-building the DOM one node at a time) are so ridiculous that I'm not even going there.

Sometimes document.write is the thing to use. The fact that it is one of the oldest methods in JavaScript is not a point against it, but a point in its favor - it is highly optimized code which does exactly what it was intended to do and has been doing since its inception.

It's nice to know that there are alternative post-load methods available, but it must be understood that these are intended for a different purpose entirely; namely modifying the DOM after it has been created and memory allocated to it. It is inherently more resource-intensive to use these methods if your script is intended to write the HTML from which the browser creates the DOM in the first place.

Just write it and let the browser and interpreter do the work. That's what they are there for.

PS: I just tested using an onload param in the body tag and even at this point the document is still open and document.write() functions as intended. Also, there is no perceivable performance difference between the various methods in the latest version of Firefox. Of course there is a ton of caching probably going on somewhere in the hardware/software stack, but that's the point really - let the machine do the work. It may make a difference on a cheap smartphone though. Cheers!

bebopalooblog
  • 89
  • 1
  • 3
7

The question depends on what you are actually trying to do.

Usually, instead of doing document.write you can use someElement.innerHTML or better, document.createElement with an someElement.appendChild.

You can also consider using a library like jQuery and using the modification functions in there: http://api.jquery.com/category/manipulation/

Wolph
  • 78,177
  • 11
  • 137
  • 148
6

This is probably the most correct, direct replacement: insertAdjacentHTML.

Justin Summerlin
  • 4,938
  • 1
  • 16
  • 10
4

Try to use getElementById() or getElementsByName() to access a specific element and then to use innerHTML property:

<html>
    <body>
        <div id="myDiv1"></div>
        <div id="myDiv2"></div>
    </body>

    <script type="text/javascript">
        var myDiv1 = document.getElementById("myDiv1");
        var myDiv2 = document.getElementById("myDiv2");

        myDiv1.innerHTML = "<b>Content of 1st DIV</b>";
        myDiv2.innerHTML = "<i>Content of second DIV element</i>";
    </script>
</html>
  • Didn't quite work out. Don't you need tags and `<\/b>` in the script? – DarkLightA Dec 27 '10 at 10:22
  • @DarkLightA: the default source you entered is not the same as the upper one. and on the other hand it is in the javascript window! the source has both source types, html & javascript. create a html file on your desktop with that source and open it. –  Dec 27 '10 at 10:40
0

You may find this method helpful. It's a vanilla JS and it accepts either string or node on the input. Just wrote this to support Adobe Target tracking.

function documentWrite(newNode) {
  // convert string to a fragment
  if (typeof newNode === 'string') {
    newNode = document.createRange().createContextualFragment(newNode);
  }
  var body = document.querySelector('body')
  if (body) {
    body.appendChild(newNode)
  }
}
iDevGeek
  • 464
  • 5
  • 4
-1

Use

var  documentwrite =(value, method="", display="")=>{
            switch(display) {
                case "block":
                 var x = document.createElement("p");
                  break;
                  case "inline":
                 var x = document.createElement("span");
                  break;
                default:
                 var x = document.createElement("p");
              }
            
            var t = document.createTextNode(value);
            x.appendChild(t);
            
            if(method==""){
             document.body.appendChild(x);
            }
            else{
            document.querySelector(method).appendChild(x);
           
            }
        }

and call the function based on your requirement as below

  documentwrite("My sample text"); //print value inside body
  documentwrite("My sample text inside id", "#demoid", "block");  // print value inside id and display block
  documentwrite("My sample text inside class", ".democlass","inline"); // print value inside class and and display inline 
Vijay R P
  • 1
  • 1
-7

I'm not sure if this will work exactly, but I thought of

var docwrite = function(doc) {               
    document.write(doc);          
};

This solved the problem with the error messages for me.

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
Mlee
  • 1