public
Last active

Efficient outerHTML polyfill that doesn't use cloneNode(true)

  • Download Gist
outerHTML.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
/*
* outerHTML.js
* Cross-browser full HTMLElement.outerHTML implementation.
*
* 2011-11-14
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
 
if (typeof document !== "undefined" && !("outerHTML" in document.createElementNS("http://www.w3.org/1999/xhtml", "_"))) {
 
(function(view) {
"use strict";
 
var
container = document.createElementNS("http://www.w3.org/1999/xhtml", "_")
, elem_proto = (view.HTMLElement || view.Element).prototype
, xml_serializer = new XMLSerializer
, outerHTML_getter = function() {
var
node = this
, html
;
if (document.xmlVersion) {
return xml_serializer.serializeToString(node);
} else {
container.appendChild(node.cloneNode(false));
html = container.innerHTML.replace("><", ">" + node.innerHTML + "<");
container.innerHTML = "";
return html;
}
}
, outerHTML_setter = function(html) {
var
node = this
, parent = node.parentNode
, child
;
if (parent === null) {
DOMException.code = DOMException.NOT_FOUND_ERR;
throw DOMException;
}
container.innerHTML = html;
while ((child = container.firstChild)) {
parent.insertBefore(child, node);
}
parent.removeChild(node);
}
;
 
if (Object.defineProperty) {
var outerHTML_prop_desc = {
get: outerHTML_getter
, set: outerHTML_setter
, enumerable: true
, configurable: true
};
try {
Object.defineProperty(elem_proto, "outerHTML", outerHTML_prop_desc);
} catch (ex) { // IE 8 doesn't support enumerable:true
if (ex.number === -0x7FF5EC54) {
outerHTML_prop_desc.enumerable = false;
Object.defineProperty(elem_proto, "outerHTML", outerHTML_prop_desc);
}
}
} else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) {
elem_proto.__defineGetter__("outerHTML", outerHTML_getter);
elem_proto.__defineSetter__("outerHTML", outerHTML_setter);
}
 
}(self));
 
}

T-shirt idea: “0x7FF5EC54 is just a fancy way of saying 2146823252.”

Wouldn't it be more efficient if you cached container instead of creating a new element every time?

Not every element is in the same document, as you can see by my use of node.ownerDocument.createElement.

Have you tested this in a recent version of Firefox? FF 6.0 on OS X seems to be removing the node without replacing it: http://jsfiddle.net/M27UA/

Fixed. For some reason I had parent.removeChild(child); in there, and I probably should have at least tested the script once I added the setter :P

Nice, thanks for the quick fix!

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.