Don't Inject Markup in A Web Page using JavaScript document.write
Look around just about every consumer facing site you visit these days has a third party script reference or out of date library.
Often these scripts use obsolete methods and techniques, like document.write, that hurt your page load times and user experience.
Just about everyone uses Google Analytics (GA) and if you are like a former client of mine you have GA and 6 other traffic analysis and third-party service scripts injected in each page.
If it is not an analytics script it is an ad script, twitter stream or hosted content service.
Third-party scripts and old libraries are common and the source of many web performance problems.
Many pages use obsolete scripts and libraries. Recent news showed about 95% of web sites load jQuery. But over 90% of those use jQuery version 1.x or 2.x, both long since deprecated.
They can ultimately cost your business money or productivity.
In additional to excessive page load times these scripts often use bad coding techniques. A common issue with code you don't own is the use document.Write to inject markup in your page.
Of course external scripts are not the only offenders, many developers use document.write their own sites.
How document.write Affects Your Page Load
One of the reasons why you should move all script references to the bottom of the page is document.write.
When the browser hits a script block or reference it stops everything till the script is load, parsed and executed.
A big reason why browsers do this is because they assume document.write is used in the script.
When document.write is called the DOM is manipulated in a way that forces the browser to completely reload the markup and re-render it.
This means anything that has been parsed, rendered, etc must be wiped out and replaced from the beginning.
This usually causes a noticeable flash during a page load. Even if you do not see the flash the page you are loading takes longer than it should without the document.write in play.
Because of this you should avoid using document.write in your scripts.
Avoid 3rd party scripts that force you to place script references to them where you want their markup to be rendered.
Handling Dynamically Added Markup
So how should you handle dynamically adding markup to your page?
Instead of using a script that performs a document.write you should use a more conventional strategy.
One technique is to use a document fragment to inject markup. The createDocumentFragment technique is extremely useful when adding dynamic content to a web page.
Instead of requiring an inline script reference require an element target.
In this example I use a DIV that contains an H1 tag just for good measure.
The CSS class is the selector hook, but you could just as easily use an ID. Personally I like CSS classes because that gives me a few more options.
Dynamic Content
createFragment is a helper method that can be reused to insert elements or a string of markup in the DOM. It does is create a document fragment containing the desired markup and returns that fragment.
The fragment can then be used in a variety of ways, for the purposes of this demonstration it will be appended as a child element of the target element.
function createFragment(htmlStr) { var frag = document.createDocumentFragment(), temp = document.createElement('div'); temp.innerHTML = htmlStr; while(temp.firstChild) { frag.appendChild(temp.firstChild); } return frag; }
Next I get a node (element) reference to the target wrapper element.
Notice I do not use jQuery to select the element, but rather the native querySelector method. This is much faster than the jQuery selector.
var target = document.querySelector(".ajax-target");
If you have multiple targets you will want to use querySelectorAll. Just remember querySelectorAll returns a nodeList you need to loop through where the querySelector returns the first matching element.
The next step is to insert the new document fragment in the target wrapper. The element.appendChild method is used, which accepts a document fragment and inserts it within the element (our wrapper).
The new fragment will be inserted as the last child element, which in this example will be after the H1 element.
To keep the example relatively simple I am passing in a string. This could just as easily be markup returned in an AJAX call or directly loaded HTML. The HTML string will be turned into a fragment to insert.
target.appendChild(createFragment( "Hello world!!! I am very dynamic!
" ));
The result is a solution that will dynamically inject markup in the page at the desired location.
Because it targets a specific HTML node(s), the technique eliminates the need to put a script reference in the markup.
Remember, referencing scripts in the middle of the page is a bad practice because it stops the browser from rendering the page.
"document.write can be a form of eval"
One of the fundamentals of using JavaScript is knowing eval is evil.
This is because eval invokes the compiler at run-time. The method takes a string, which is typically more script.
A reason why this is frowned upon is it can be used to inject evil scripts into the page.
document.write can also be used to inject scripts are run-time:
document.write(''); alert(eval(1+2));
This technique is a way bad third party service providers slip bad code into your page.
Another reason to avoid using document.write and third party scripts that do.
Summary
The createDocumentFragment method eliminates the dependency on any third party library (like jQuery) that may or may not be loaded.
In the end the page will load faster, not have a flash or blink during load and the script will not have a dependency on a library that may or may not be loaded. I created a jsFiddle so you can see the code in action, http://jsfiddle.net/docluv/vczpT/.
document.write can also be an attack vector for bad guys to use your pages for bad things.