Cada día son más comunes en la web las aplicaciones AJAX, es decir, que descargan contenidos dinámicamente desde el servidor.

Sucede que para ecribir esos contenidos en la página actual el método más usado, si se descargó HTML desde el servidor, es empleando la propiedad .innerHTML desde ECMAScript. Esta solución, aunque satisfactoria porque funciona en probablemente todos los navegadores mayoritarios, no se ajusta a los estándares web, todavía.

Lo primero es entender cómo funciona el atributo innerHTML. Lo que hace es pasarle al navegador una cadena de texto conteniendo el código HTML a insertar dentro de un elemento. Aquí radica, precisamente, la versatilidad del atributo.

Me tomó un tiempo lograr algo similar hasta que me topé con la interfaz DOMParser.

Cada día son más comunes en la web las aplicaciones AJAX, es decir, que descargan contenidos dinámicamente desde el servidor.

Sucede que para ecribir esos contenidos en la página actual el método más usado, si se descargó HTML desde el servidor, es empleando la propiedad .innerHTML desde ECMAScript. Esta solución, aunque satisfactoria porque funciona en probablemente todos los navegadores mayoritarios, no se ajusta a los estándares web, todavía.

Por ésto, vengo a proponer una alternativa que puede resolver el problema.

Lo primero es entender cómo funciona el atributo innerHTML. Lo que hace es pasarle al navegador una cadena de texto conteniendo el código HTML a insertar dentro de un elemento. Aquí radica, precisamente, la versatilidad del atributo.

Me tomó un tiempo lograr algo similar hasta que me topé con el objeto DOMParser, que no es soportado por MSIE, pero que se dispone de un ActiveX alternativo, DOMDocument.

Entonces, lo primero es tener una interfaz transparente para usar el método estándar DOMParser o el ActiveX DOMDocument.

function XMLParser() {
var xmlDOM=null;

if (window.DOMParser) {
xmlDOM = new window.DOMParser();
} else if (window.ActiveXObject) {
var progIDs = [ 'Msxml2.DOMDocument.6.0', 'Msxml2.DOMDocument.3.0',
'Msxml.DOMDocument', 'Microsoft.XMLDom' ];

xmlDOM=new Object();
var xmlAX;

for (var i = 0; i < progIDs.length; i++) {
try {
xmlAX = new ActiveXObject(progIDs[i]);
} catch (err) {};
}

xmlDOM.AX=function() {
return xmlAX;
};

xmlDOM.parseFromString=function(s,t) {
xmlAX.loadXML(s);
return xmlAX;
};
}
return xmlDOM;

Luego, donde antes teníamos elemento.innerHTML=texto; se debe poner lo siguiente:
if (typeof texto=="object")
xmlDoc=text;
else {
var DOMParser=XMLParser();
var xmlDoc=DOMParser.parseFromString("<div xmlns='http:\/\/www.w3.org/1999/xhtml'>"+texto+"</div>","application/xml");
}

while (xmlDoc && xmlDoc.hasChildNodes()) {
lay.appendChild(document.importNode(xmlDoc.firstChild, true));
xmlDoc.removeChild(xmlDoc.firstChild);
}

Para que funcione bien en MSIE, además, se debe agregar lo siguiente:
if (!document.ELEMENT_NODE) {
document.ELEMENT_NODE = 1;
document.ATTRIBUTE_NODE = 2;
document.TEXT_NODE = 3;
document.CDATA_SECTION_NODE = 4;
document.ENTITY_REFERENCE_NODE = 5;
document.ENTITY_NODE = 6;
document.PROCESSING_INSTRUCTION_NODE = 7;
document.COMMENT_NODE = 8;
document.DOCUMENT_NODE = 9;
document.DOCUMENT_TYPE_NODE = 10;
document.DOCUMENT_FRAGMENT_NODE = 11;
document.NOTATION_NODE = 12;
}

if (!document._importNode) document._importNode = function(node, allChildren) {
try {   /* find the node type to import */
if (node.nodeName.toLowerCase()=="noscript") return -1;
var attrN,attrVal;
switch (node.nodeType) {
case document.ELEMENT_NODE:
/* create a new element */
var newNode = document.createElement(node.nodeName);
/* does the node have any attributes to add? */
if (node.attributes && node.attributes.length > 0)
/* add all of the attributes */
for (var i = 0, il = node.attributes.length; i < il;i++) {
attrN=node.attributes[i].nodeName||node.attributes[i].name;
attrVal=node.attributes[i].value||(node.getAttribute?node.getAttribute(attrN):0);
if (attrVal && attrVal.length) {
if (attrN.toLowerCase().substr(0,2)!="on") {
switch(attrN.toLowerCase?attrN.toLowerCase():attrN) {
case "class":
newNode.className=attrVal;
break;
case "for":
newNode.htmlFor=attrVal;
break;
default:
if (newNode.setAttribute)
newNode.setAttribute(attrN,attrVal);
break;
}
} else {
addEvent(newNode,attrN.substr(2),new Function(attrVal));
}
}
}
/* are we going after children too, and does the node have any? */
var impRes;
if (allChildren && node.childNodes && node.childNodes.length > 0)

/* recursively get all of the child nodes */
for (var i = 0, il = node.childNodes.length; i < il;) {
impRes=document._importNode(node.childNodes[i++], allChildren);
if (impRes!=-1) newNode.appendChild(impRes);
}
return newNode;
break;
case document.TEXT_NODE:
case document.CDATA_SECTION_NODE:
case document.COMMENT_NODE:
return document.createTextNode(node.nodeValue);
break;
}
} catch(e) {alert("err here");throw e;};
};

if (!document.importNode && document._importNode) document.importNode = document._importNode;

Crédito por buena parte del último fragmento de código a A List Apart.