Making stuff as a founder of Avocado. Former music-maker. Tuna melt advocate. Started Google Reader. (But smarter people made it great.)

Fragments are tricky.

XML handling and manipulation with Javascript seems a breeze to develop.

So long as the breeze I'm using for comparison runs around pants lowered, screaming and defecating.

Recently I've danced the cross-browser shuffle around DocumentFragments. (whuzh?) I really should explain ... a DocumentFragment is a special type of node that serves as a temporary container for a collection of nodes and allows those nodes to be manipulated as a single object. When a DocumentFragment is inserted into a document, it isn't the DocumentFragment itself that's inserted, but each of its children.

I have a bunch of uses for these. Mainly, for when I want to append the content of some node to an HTML element. If that content is HTML, then I'd like it to render faithfully. So I'd hope that there's a simple cross-browser method somewhere in the DOM I can leverage.

You... you can see this coming. Same way you knew how The Village was gonna end.

Currently the Big 3 (1. IE, 2. Moz/Gecko, 3. Safari/WebKit) have differing levels of support for generating HTMLElement-friendly fragments. Lemme break it down...

1. Moz is the path of least resistance.

Faboo. And by faboo I mean you can take a node with HTML children and append it to some HTMLElement via innerHTML or appendChild() and you're done.

2. IE needs a small assist.

IE is pretty darn close. But the node value needs to be laundered. The solution lies in creating an element and setting its innerHTML to the serialized value of the node, which then allows us to retrieve the node's value as HTML.

3. Safari...it's dark, it's cold, and I'd like my blank-ey now.

So...a disclaimer, first. Safari's still shiny-new in terms of browser life and it's okay that all my whack-job, bleeding-edge XML handling desires aren't there. Heck, I'm perfectly content with the way development over there is going and I'm especially excited about their next release. (Especially after getting to talk with some of their developers at a meeting we had at Google. What a nice perquisite to my job.) So, here's the rub, currently. There's no DOM serializer in Safari, right now. So we need to iterate through each child node and add to a concatenated string either its innerHTML or nodeValue property. Then by creating an text node and setting its value to the concatenated result we're allowed to append the node's value as HTML.

Sounds simple? <sarcasm>Yep.</sarcasm> I've included a slapped-together method for approaching the problem. It's small, though not flexible in scope. And I, perhaps like you, struggle with IE's non-support for node type constants. Nevertheless, I just needed something to help test the behavior and move past this problem to others. Please feel free to use this code if it helps you in some way. And, hey, if you have a different solution to the problem ... just drop a line or comment here, that would be helpful.

(In my example I presume the use of some Detect object to differentiate the browsers. That's just the way my code base is currently constructed for various reasons.)

/*
* For generating HTML documentFragments that can be
* appended to HTMLElements.
*/

// block scope array for storing nodes and values
// from getInnerHTML
var htmlConcat = [];

/*
* getInnerHTML()
*
* Populates htmlConcat recursively with values for
* a given object. Useful for Safari. Needed for
* makeFragment() to work.
*/
function getInnerHTML(o) {
if (o.hasChildNodes()) {
for (i=0; i
var type = o.childNodes[i].nodeType;
var innerHtml = o.childNodes[i].innerHTML;
if (innerHtml) {
htmlConcat.push(innerHtml);
} else if (type == 1) {
getInnerHTML(o.childNodes[i]);
} else if (type == 3) {
htmlConcat.push(o.childNodes(0).nodeValue);
}
}
}
}

/*
* makeFragment()
*
* Returns a documentFragment from a given node.
*/
function makeFragment(o) {
if (Detect.IE() || Detect.SAFARI()) {
var tmp = document.createElement("div");
document.body.appendChild(tmp);
tmp.style.display="none";

if (Detect.SAFARI()) {
getInnerHTML(o);
var html = htmlConcat.join('');
var tmp2 = document.createTextNode(html);
tmp.appendChild(tmp2);
} else {
tmp.innerHTML = o.xml;
}

o = tmp.childNodes[0];
tmp = null;
}
return o;
}
Next up: I use this in something and maybe even talk about that as well. (Friends and family yawn. :)
posted at August 2, 2004, 4:38 AM

2 Comments:

  • At 8:13 PM, Chris Wetherell said…

    This post has been removed by the author.

     
  • At 11:17 AM, Anonymous said…

    A very helpful post. I have been hoping to create a form of client-side include so that I can have a remote site include a JS script file and call a method to inline some content, much like Google AdSense. It all works great except in Safari. It seems I cannot access an XML node via firstChild.nodeValue. You can see my test page...

    http://brennan.offwhite.net/xmlhttp/

    My question, how can I find out how rich the support there is in Safari and other browsers? As yet I only know of the DOM reference for Mozilla which I follow first, but I would like to know how Safari and MSIE stack up.

    Brennan Stehling
    http://brennan.offwhite.net/blog/

     

Post a Comment