Note: this code is very old and is no longer supported and really isn't needed since
application/xhtml+xml is dead. This writeup and code is left here for historical reasons. Only one example still works due to changes in Google's API offerings. I've also stored the old comments I got on my blog post before I started redirecting here to GitHub.
Update 2008-10-10: Minor fix for "parse error" issue that arose in Firefox 3.0.3 (thanks pinkduck).
Update 2008-06-05: Made modifications to
HTMLParser to support more usages, and reverted the changes to it that turned the local variables into member properties since it wasn't necessary.
Update 2008-06-04: Added discussion about supported usages.
Google's AJAX APIs provide some incredible tools which equip web developers to do some amazing things. I've lately been dazzled by the power and accuracy of their AJAX Language API and also of the performance and convenience afforded by their AJAX Libraries API. The only issue I have with their APIs is that the fundamental
google.load() function uses
document.write() to output the necessary
script elements to the DOM, which doesn't even appear to be necessary since
google.setOnLoadCallback() executes after the scripts are loaded without using
document.write() (but instead appended document with DOM methods). And the reason why using
document.write() is bad, of course, is that it is not available when in XHTML.
Likewise, Google's AdSense program provides a great way for web authors to make get some compensation for their hard work. But it too relies on
document.write() to output the necessary
iframe element to display the advertisement. This has been well noted, and a workaround has been developed which utilizes the an
object element. However, there is another solution which enables AdSense to work in XHTML without any HTML workaround, and which allows web authors to use the Google Ajax APIs in XHTML pages: simply define and implement
document.write() yourself, as this script does.
innerHTML which causes it to completely fail in Safari 2 (although this implementation also fails for an unknown reason). I'm trying a different approach. Instead of using
innerHTML, this implementation of
document.write() parses the string argument of HTML markup into DOM nodes; if the DOM has not been completely loaded yet, it appends these DOM nodes to the document immediately after the requesting
script element; otherwise, it appends the parsed nodes to the end of the
I've incorporated John Resig's own HTML Parser (via Erik Arvidsson), but I've made a couple key modifications to make it play nice with
document.write(). I turned
HTMLParser into a class
with member properties in order to save the end state of the parser after all of the buffer has been processed. To this class I added a
parse(moreHTML) method which allows additional markup to be passed into the parser for handling so that it can continue parsing from where it had finished from the previous buffer. And by removing the last
parseEndTag() cleanup call (for
document.write() is anything but clean), it then became possible for multiple
document.write() calls to be made with arguments consisting of chopped up HTML fragments like just a start tag or end tag, which is exactly what AdSense does and is a common usage of the method.
document.write() implementation is known to work at least in Firefox 2/3, Opera 9.26, and Safari 3. It will work in Internet Explorer, of course, since the document must be served as
text/html to be viewed and so it will already have
document.write(). For some unknown reason, this currently does not work in Safari 2: the error “TypeError - Undefined value” is raised on the line of HTML where a
script element loads
xhtml-document-write.js. Any help would be much appreciated. As a workaround in the mean time, simply serve documents as
text/html for Safari 2 browsers.
There are three common usages of
document.write() in the wild of HTML, and the first two are currently supported:
Outputting a well-formed HTML code fragment:
This usage is fully supported by this implementation.
Outputting a well-formed HTML code fragment spread out over multiple sequential function calls:
document.write('<p>'); document.write('Hello <i>World</i>!'); document.write('</p>');
This usage is also supported
scriptelements with function calls outputting HTML fragments interspersed by arbitrary HTML elements:
This is not supported. Instead of outputting “Hello World!”, this implementation would output one empty
belement, followed by just “Hello World!” This usage is more difficult to support although I have an idea of how to do it, but I may not end up implementing it unless there is demand for it.
One restriction, of course, to the use of this implementation is that the entire document must be well-formed XHTML without regard for the markup output by the calls to
document.write(); thus you cannot do something like this (via Ian Hixie):