innerXHTML
Web developers working with Javascript and the Document Object Model will know that development can often be a longwinded process. The large selection of standardised tools we have available at our disposal, as stipulated by the W3C, allow us to manipulate the DOM with an unprecedented level of precision. However this precision comes at the expense of time, and unfortunately the W3C were not so generous in providing tools which could forfeit precision for speed and ease of use.
Enter Microsoft with it's non-standard innerHTML function. innerHTML benefits for all the latter reasons; it is easy and incredibly quick to use. Despite lacking in precision it remains a popular tool of choice for many DOM scripters. Even its exemption from the W3C's specification has not deterred many web developers from utilising it. innerHTML enjoys good cross-browser support, but like all non-standard aspects of web design this is unlikely to remain so come future advancements in browser technology. Such change will no doubt render all script using innerHTML inoperable. We need to find an alternative; a standardised innerHTML.
What is innerXHTML?
innerXHTML is a glorified DOM read/writer. It is my answer to the non-standard innerHTML, the result of too many late nights in front of the computer screen and the aspirin to many of my DOM scripting headaches. It utilises existing functionality, all supported by the W3C, to read and write to the DOM. In doing so, innerXHTML gains all the benefits of innerHTML without the baggage; speed and ease of use, whilst keeping within the boundaries of global standardisation.
innerXHTML is not a document copier; it does not fetch exactly what the document contains, character for character in a zombie-like fashion. innerXHTML reads the document node tree, returning a text string of markup to the document's equivelent. Likewise, innerXHTML writes to the DOM with the correct approach; intelegently, node by node, not as one big string. You wont find any document.write() here!
How does innerXHTML work?
It is split into two functions. The first function innerXHTML acts as the interface between the two. It reads the document node tree and returns markup in plain text to the source that called it. The second function translateXHTML serves the purpose of translating XHTML and XML code from a plain text string into nodes for insertion into the document object model. translateXHTML is the more processor-heavy function and is additionally more complex in structure. You can operate translateXHTML independently from innerXHTML, though I don't recommend this.
Using innerXHTML to read from the DOM
I have built innerXHTML to work much like innerHTML. Once the .js file is referenced by your markup document using a <script src=""> you can use innerXHTML like any other function:
var container = document.getElementById('container');
var code = innerXHTML(container);
This snippet of script above, will find the node with an id attribute of 'container' and, using the innerXHTML function return it's entire content in a plain text string - markup 'n all. The one and only parenthesis is the mandatory source element from which we are reading. Remember, innerXHTML will return the markup of whatever nodes the source element contains. Data returned will not include the source element's own tag and attributes, mirroring the non-standard innerHTML. Lets take a quick look at the aforementioned innerHTML for execution comparison:
var container = document.getElementById('container');
var code = container.innerHTML;
As you can see innerXHTML takes a slightly different approach, but ultimately remains just as easy to use.
Using innerXHTML to write to the DOM
Whilst reading has its benefits, innerXHTML really comes into its own over regular, hand-written document.createElement() techniques when writing to the DOM. Traditionally every element, and sub-element, and sub-sub-element has to be individually created by you the developer, in addition to each element's potentially multiple attributes and contained text nodes. This is a labouring process to say the least.
Using innerXHTML all you are required to provide is the plain string of markup. The function will do the hard work of reading and translating the markup into DOM nodes and inserting them into your document for you. Let's take a look:
var container = document.getElementById('container');
var markup = '<div id="content"><p class="text">Hello world!</p></div>';
var code = innerXHTML(container,markup);
Our innerXHTML function accepts two parentheses this time; the mandatory source element in which we are writing, and secondly the string of text markup to replace any existing content therein. The above code fragment finds the element with an id of 'container' and, via innerXHTML, inserts into it a <div> with an id attribute of 'content'. Into that it appends a <p> paragraph element with a child text node of 'Hello World!'. Don't worry about any document.createElement(); messing about; innerXHTML takes care of this all for you. The variable code here is optional and will now contain exactly the same text markup as you put into the function.
Some people use innerHTML for the purpose of quickly emptying an element of its children. This is a perfectly good use of innerXHTML with a command such as innerXHTML(container,' ');. Just be aware that a value must be passed for the second parenthesis; ideally, a space. Technically this means you are not emptying the source element completely, but rather replacing its content with a single space text node. However for practical use this should serve the purpose.
Additional Informaiton
I have repeatedly streamlined innerXHTML to run as efficiently as possible, and from earlier (you could call them alpha ;) ), development versions the results do show. innerXHTML is quick in operation and can construct full pages from markup easily, depending on the computer and browser speed. A good example of this is the developing Hasselhoff Is God website, which now uses innerXHTML (replacing innerHTML) to read and replace the entire document on page load.
Please remember that this function expects only code adhering to web standards. Passing the function non-standard markup will almost certainly result in process failure!
Additionally I have taken time to ensure innerXHTML is as cross-browser compatible as possible, hence some structural changes along the development line. Below is a table of browser support information. Please note that the browsers listed below are the only ones that I have tested innerXHTML on. If a browser you are looking for is not listed here then it will not mean that innerXHTML does not run, it simply means I have not tested it as yet. If you do undertake any further testing and have feedback then please get in touch with me. It's all good :)
| Browser | Supported? | Additional Comments |
|---|---|---|
| FF Windows | Full support | |
| IE6 Windows | Virtually Full support | 'on' events (onclick, onmouseover, etc.) do not work when translated from text into document.Fixed in v0.4+. Full support |
| Flock Windows | Full support | |
| FF Mac | Full support | |
| Safari Mac | Full support | |
| Camino Mac | Full support | |
| Opera Windows | Full support | Writes elements using uppercased tag and attribute names, and enters unnecessary attributes such as shape="rect". However, further testing has found this causes no fracture to the functionality and the differences are almost certain to go unnoticed (unless, like me, you are looking for them!). |
| IE5 Windows | Virtually full support | In addition to the one IE6 error (above) IE5 does not translate <!--comments-->. However markup created on-the-fly with JS does not appear in the source document anyway, so unless you plan to insert markup then read it back into a string, you're not going to notice a thing :) |
Important: innerXHTML translates and inserts XML into the DOM without a problem in all browsers. It reads and returns XML correctly in most browsers too; every one except Microsoft Internet Explorer 5&6. This is entirely down to poor DOM support by IE. For the same reason IE 5&6 will not read any part of the document node tree containing element(s) it does not recognise. In practical use this basically means by example that innerXHTML will not read any part of the document tree containing the <abbr> element in Internet Explorer 6 or less. Internet Explorer 7 has corrected the problem.
Update 21/03/2007 - Version 0.4 Released
Almost 6 months following it's release innerXHTML has transcended to version 0.4. The updates make the function slicker, easier and more cross-browser compatible than ever. They are:
- $appendage argument added
-
The function now accepts three arguments rather than just two. The third argument is $appendage and can dictate where in the parent node you place your inserted markup string. Let's use this document slice as the basis for our examples:
<div id="content">
<h2>About Google</h2>
<p>Google was co-founded by Larry Page and Sergey Brin while they were students at Stanford University.</p>
<p id="closing_paragraph">The name "Google" originated from a misspelling of "googol", which refers to 10<sup>100</sup> (the number represented by a 1 followed by one hundred zeros).</p>
</div>innerXHTML's third argument now accepts four value types:
- Default boolean false: Empties the parent container.
- Child node: Enter a child node and the markup string you pass will be inserted before it.
- String ID: As above, except the child is located using the ID of the string you pass.
- Any other true value: Append the string markup you pass after existing parent's children.
Respective examples:
// This will empty the DIV with a id of 'content' before inserting the link
innerXHTML(document.getElementById('content'), '<a href="#">click me</a>');// This will insert the link before the first paragraph
innerXHTML(document.getElementById('content'), '<a href="#">click me</a>', document.getElementsByTagName('p')[0]);// This will insert the link before the second paragraph
innerXHTML(document.getElementById('content'), '<a href="#">click me</a>', 'closing_paragraph');// This will append the link into the DIV after the existing content
innerXHTML(document.getElementById('content'), '<a href="#">click me</a>', true); - First $source argument now accepts string ID
-
Speed things up by entering the parent id as the first argument, rather than a node reference. Using the last snippet as an example:
innerXHTML('content', '<a href="#">click me</a>'); - 'on' events now supported by all browsers
-
Does what it says on the tin: Internet Explorers now also support inserted on events. Example:
innerXHTML('content', '<a href="#" onclick="alert(\'Hello World\');">click me</a>');
I'd like to thank Stef Dawson for his most appreciated contributions and feedback. He's made the 'on' event patch in IE a reality - cheers Stef :).
If anyone else has any ideas for improving innerXHTML then don't hesitate to let me know. Let's put an end to this non-standard bollocks once and for all.
Callin' it a do
Take a copy of the innerXHTML script and feel free to leave any comments or thoughts you may have. At 150 lines innerXHTML weighs in at just under 6kb and shouldnt burden your website much. I've released this software under the Creative Commons Attribution-Share Alike 3.0 License.
It is intended only to reduce DOM script development time, and I hope this is what it achieves. If you do happen to uncover any elusive bugs I have missed then please drop me an email. Otherwise enjoy :)


















On 15/10/2006 P.J. Onori said:
Wow, this seems very impressive. As someone that has been forced to do more Javascript than is within my comfort zone, something like this is a huge plus. Awesome writeup man.
On 15/10/2006 rasmus said:
a wee bit shorter method for mozilla browsers:
Element.prototype.innerXHTML = function (xhtml) {
if (xhtml) {
for (var i=0, n; n=this.childNodes[i]; i++) this.removeChild(n);
var r = document.createRange();
r.selectNode(this);
var x = r.createContextualFragment(xhtml);
this.appendChild(x);
return x;
}
else {
var xhtml = "";
for (var i=0, n; n=this.childNodes[i]; i++) xhtml += (new XMLSerializer()).serializeToString(n);
return xhtml;
}
};
used like this:
var xhtml = "<p>hello, world</p>";
var element = document.getElementById("MyDiv");
element.innerXHTML(xhtml);
if no arguments are passed to the innerXHTML method it returns the existing html from the element.
alert(element.innerXHTML());
// alerts "<P>hello, world</P>".
On 16/10/2006 Nate Cavanaugh said:
For those of us who use Prototype (http://prototype.conio.net), here is a handy tip for integrating this function to the DOM methods.
Add this to your javascript:
Element.addMethods({
innerXHTML: function(element, content) { return innerXHTML(element, content); }
});
Now you can quickly read and set the innerHTML of the document like this:
$('container').innerXHTML(); // read the innerHTML
$('container').innerXHTML('<div>test</div>'); // set the innerHTML
Just thought it might help some folks :)
On 16/10/2006 Fredrick said:
Great piece of javascripting Steve!! Maybe we will be seeing the last of innerHTML sooner than we all thought... ;-)
On 16/10/2006 Stoyan said:
Good job, thanks for sharing!
I tried to address the issue with a tool to create DOM calls from HTML, sort of like code generator. My approach doesn't use regex, but tries to create an XML document from the HTML string, therefore forcing you to pass only valid XHTML, otherwise it won't work. My tool is at http://www.html2dom.com/
On 17/10/2006 Jonathan Snook said:
The chances of innerHTML disappearing from browsers is slim to none. If anything, it'll simply become a part of future standards.
Good job on the script, though. :)
On 17/10/2006 Steve Tucker said:
Thanks for the positive comments guys :)
@Jonathan: I wish they would make innerHTML a standard - would make life for DOM developers a whole lot easier
On 17/11/2006 Adam said:
Thanks Man...you just made my week.
On 21/11/2006 pilyoto said:
Thanks for sharing your work! About the 'on' events not working, is there any way to get around this?
On 23/11/2006 Steve Tucker said:
Not directly in Explorer - ive tried. The only way to do on events is by executing your innerXHTML call, then adding the on event to the element afterwards using, say, an ID as a handle.
On 03/12/2006 Tommy said:
Awsome work. This is going to be really useful!
Keep it up
On 07/12/2006 Florian said:
Great work Steve, thanx for sharing!! What is the load time for this function in comparison to innerHTML? F.
On 31/01/2007 Chris said:
I've tried using this method on flash markup, such as <object...><param...><embed...> and it doesn't work. Firefox doesn't do anything, IE6 reports Unexpected call to method or property access. If I use markup like <p>some text</p>, it works fine. Any suggestions?
On 20/03/2007 Bloke said:
Awesome function, thank you: just what I've been after. I've extended it slightly though:
1) Added a third 'append' argument. If true, the contents of the source element are not cleared; although not truly in the spirit of innerHTML, it's particularly useful for logging purposes or showing users a history of what they've done.
2) The source argument can now be a string as well; if it is, the element is looked up via a call to document.getElementById(). Again, just a convenience.
3) I've kludged a way to handle 'on' events in IE6. It's not pretty and I'd love to find a way of making it dynamically use the attName as the handler instead of the ugly switch() statement. Maybe someone else can do it? I spent the afternoon hacking it this far and I'm sick of the sight of IE6!
It does the setAttribute as normal for compliant browsers and then adds an anonymous function directly to the element's onclick for IE6 and other naughty/stupid browsers.
btw, the $ in front of the variable names played havoc with eval() and the regexes as I was testing so I removed it. Upon reflection I guess it can go back in (possibly with some escaping) if you miss it.
Have a look at the code at http://www.stefdawson.com/downloads/innerXHTMLplus.js
Thanks again, this function has saved me tonnes of repetitive DOM programming, with the advantage that the DOM structure is retained - superb!
On 21/03/2007 Steve Tucker said:
Thanks, Stef! That's some really great work. It's uncanny that you submitted this now, because I've been about to release v0.4 with $append argument you suggested, only with a little more flexibility (see documentation update for details).
Really awesome work though with the 'on' events - I'd never have though about eval as a way around that problem in IE. I've fixed the dynamic event setting too, so it's much cleaner and more flexible. Check the source code.
Also, unusually, I had no such problems with the $ character I use on variable names. Very strange?
Cheers mate :)
On 22/03/2007 Jerry said:
Thanks so much for sharing this its going to save me loads of programming time!
On 23/03/2007 Thor Larholm said:
I don't see why you would not add this a a property on all the HTML elements. Here is a simple addition to your script that defines a gettable and settable property in IE and moz/firefox:
For mozilla, add this script
<script>
if(typeof HTMLElement!="undefined" && typeof HTMLElement.prototype!="undefined" && typeof HTMLElement.prototype.__defineGetter__=="function"){
HTMLElement.prototype.__defineGetter__("innerXHTML",function(){ return innerXHTML(this) });
HTMLElement.prototype.__defineSetter__("innerXHTML",function(S){ return innerXHTML(this,S) });
}
</script>
For IE, add this style declaration:
<style>* { behavior: url(innerxhtml.htc) }</style>
innerxhtml.htc contains:
<PUBLIC:PROPERTY NAME="innerXHTML" PUT="putxhtml" GET="getxhtml"/>
<script>
function putxhtml(S){ return innerXHTML(element,S) }
function getxhtml(){ return innerXHTML(element) }
</script>