innerXHTML — Revisited

6th August 2005

After writing a seriously over-complicated JavaScript class to handle innerHTML style behaviour in XHTML documents served with the application/xhtml+xml MIME type. I just found out what I was doing wrong that was preventing all the simpler things I tried working. It’s all about namespaces.

I found the solution on Bugzilla while searching for something entirely unreleated. If the DOM tree created by the DOMParser object’s parseFromString method doesn’t use the XHTML namespace the browser won’t display the nodes with their usual XHTML characteristics — without the namespace it is just plain ol’ XML where a p tag could have an entirely different meaning to p in XHTML. If when the nodes are created by DOMParser we use the namespace to state explicitly that the nodes are XHTML they can easily be inserted into the document where they will be displayed as intended. The issue with namespaces would also apply to XML coming from other sources, such as responseXML when using XMLHttpRequest.

The whole original class can be replaced by the following bite-sized function,

function innerXHTML(str, replace, target) {
    if (typeof target == 'string') {
        var target = document.getElementById(target);
    } else if (target == undefined) {
        var target = document.getElementsByTagName('body')[0];
    }

    if (!target) {
        return false;
    }

    if (window.DOMParser) {
        try {
            var dom = new DOMParser().parseFromString(
                '<div xmlns="http://www.w3.org/1999/xhtml">' + 
                str + 
                '</div>', 'application/xhtml+xml');
            if (dom.firstChild.nodeName == 'parsererror') {
                return false;
            }
        } catch(e) {
            return false;
        }

        if (replace) {
            while (target.hasChildNodes()) {
                target.removeChild(target.firstChild);
            }
        }

        for (var i = 0; i < dom.firstChild.childNodes.length; ++i) {
            target.appendChild(
                document.importNode(dom.firstChild.childNodes[i], true)
            );
        }

    } else {
        if (replace) {
            target.innerHTML = str;
        } else {
            target.innerHTML += str;
        }
    }
    return true;
}

Which can be used in the same way as the write method of the original class. str is a string of well-formed XHTML. Note that it doesn’t require a single root node, so something like,

<p>hello</p><p>world</p>

Is perfectly ok to use. replace is true or false, and indicates whether the content being added should replace any existing content in the element it’s being inserted to, or be appended to the end of it. Finally target is the element to add the content to. It could either be a string — in which case it’s assumed to be the id of an element in the page, or it could be a reference to an element. If it is omitted it defaults to the body. The function returns false on error — if either the target doesn’t exist or the string couldn’t be parsed into a DOM tree and it returns true if everything was successful.

Permalink. Posted on 6th August 2005 in Browsers, JavaScript, Web Standards.

Comments

  1. That's a nice little function there, saved me writing one, and it looks to be working fine.
    My ajax website redesign can now work with xhtml instead of forcing me to revert to HTML. good news.
    Thanks

    # Posted by Josh on 6th February 2006.

Sorry, comments for this item are currently closed.

Of Interest

Hangouts

Listening