37

Part of my code I get the OuterHTML propery

"<LI onclick="TabClicked(this, 'SearchName', 'TabGroup1');">Name "

so I can do stuff involing parsing it.

There is no OuterHTML property in javascript on firefox though and I can't find an alternative way to get this string. Ideas?

Tyler
  • 21,762
  • 11
  • 61
  • 90
NibblyPig
  • 51,118
  • 72
  • 200
  • 356
  • 1
    From your example it is not clear what would you like to accomplish and where do you use this property. – Darin Dimitrov Nov 09 '09 at 13:15
  • All I want is the text in the OnClick event as a string, so I can do things with it. – NibblyPig Nov 09 '09 at 13:49
  • If all you want is the onclick, then use elm.getAttribute("onclick"). – Marius Nov 09 '09 at 14:13
  • Yeah you'd think so, but heavy googling showed me getattribute was buggy and broken in every browser, and getattributenode was the solution;) – NibblyPig Nov 09 '09 at 15:32
  • 1
    Okay, then use getAttributeNode instead. That still doesn't explain why you want outerHTML (although there are other reasons you might want it, of course) – Tyler Aug 26 '10 at 21:57
  • The reason I wanted this was because I had some javascript that scanned for onclick events and did some funky stuff with it, to make a tab control. It was a while ago, and I didn't know jquery =) – NibblyPig Dec 06 '11 at 15:10

11 Answers11

68

Here's the function we use in pure.js:

function outerHTML(node){
    return node.outerHTML || new XMLSerializer().serializeToString(node);
}

To use it the DOM way:

outerHTML(document.getElementById('theNode'));

And it works cross browsers

EDIT: WARNING! There is a trouble with XMLSerializer, it generates an XML(XHTML) string.
Which means you can end up with a tags like <div class="team" /> instead of
<div class="team"></div>
Some browsers do not like it. I had some pain with Firefox 3.5 recently.

So for our pure.js lib we came back to the old and safe way:

function outerHTML(node){
    // if IE, Chrome take the internal method otherwise build one
  return node.outerHTML || (
      function(n){
          var div = document.createElement('div'), h;
          div.appendChild( n.cloneNode(true) );
          h = div.innerHTML;
          div = null;
          return h;
      })(node);
  }
Mic
  • 24,812
  • 9
  • 57
  • 70
  • Thanks, I used your last method! – Web_Designer May 14 '11 at 16:06
  • 1
    Firefox 11 will support outerHTML natively - see https://bugzilla.mozilla.org/show_bug.cgi?id=92264 – Nickolay Dec 04 '11 at 21:01
  • 1
    @Nickolay wow... a 10 year request finally answered! Thanks for the update – Mic Dec 05 '11 at 07:51
  • Golfed down: `document.createElement('_').appendChild(node.cloneNode(true)).innerHTML` – yckart Aug 07 '14 at 21:40
  • 1
    @yckart, Interesting. But Chrome does not seem to like '_', and you forgot a parentNode to get the outer tag. However something like `document.createElement('div').appendChild( n.cloneNode(true) ).parentNode.innerHTML` works quite well. Thanks! – Mic Aug 08 '14 at 12:55
  • @Mic Whoops... I had just an iPhone and my head. No chance to test it before ;) – yckart Aug 08 '14 at 14:48
18

The proper approach (for non-IE browsers) is:

var sOuterHTML = new XMLSerializer().serializeToString(oElement);
Sergey Ilinsky
  • 31,255
  • 9
  • 54
  • 56
  • 6
    Pay attention that XMLSerializer generates XML(XHTML) and then do things like `
    ` instead of `
    `. See my answer here.
    – Mic Dec 02 '10 at 22:53
12

If you are willing to use jQuery then it's relatively simple:

$('<div>').append( $(ElementSelector).clone() ).html();

This will get the outer HTML of multiple elements if multiple elements are selected.

Peter Ajtai
  • 56,972
  • 13
  • 121
  • 140
7

outerHTML is now supported by Firefox:

From Firefox 11 for developers

Firefox 11 shipped on March 13, 2012. This article provides information about the new features and key bugs fixed in this release, as well as links to more detailed documentation for both web developers and add-on developers.

  • The element.outerHTML property is now supported on HTML elements.
Rich Bennema
  • 10,295
  • 4
  • 37
  • 58
2

Try this: http://snipplr.com/view/5460/outerhtml-in-firefox/:

if (document.body.__defineGetter__) { 
   if (HTMLElement) {
      var element = HTMLElement.prototype;
      if (element.__defineGetter__) {
         element.__defineGetter__("outerHTML",
           function () {
              var parent = this.parentNode;
              var el = document.createElement(parent.tagName);
              el.appendChild(this);
              var shtml = el.innerHTML;
              parent.appendChild(this);
              return shtml;
           }
         );
      }
   }
}
Peter Ajtai
  • 56,972
  • 13
  • 121
  • 140
Mark Bell
  • 28,985
  • 26
  • 118
  • 145
  • The problem with that page is that it falls into a discussion with people saying the original example is wrong, so I am quite confused by it. – NibblyPig Nov 09 '09 at 13:44
  • Actually the guy who says it's wrong later says it does work - have you tried it? – Mark Bell Nov 09 '09 at 14:31
2

For the reason that W3C does not include outerHTML property, you just need add following:

if (typeof (HTMLElement) != "undefined" && !window.opera)  
{  
    HTMLElement.prototype._____defineGetter_____("outerHTML", function()  
    {  
        var a = this.attributes, str = "<" + this.tagName, i = 0; for (; i < a.length; i++)  
            if (a[i].specified)  
            str += " " + a[i].name + '="' + a[i].value + '"';  
        if (!this.canHaveChildren)  
            return str + " />";  
        return str + ">" + this.innerHTML + "</" + this.tagName + ">";  
    });  
    HTMLElement.prototype._____defineSetter_____("outerHTML", function(s)  
    {  
        var r = this.ownerDocument.createRange();  
        r.setStartBefore(this);  
        var df = r.createContextualFragment(s);  
        this.parentNode.replaceChild(df, this);  
        return s;  
    });  
    HTMLElement.prototype._____defineGetter_____("canHaveChildren", function()  
    {  
        return !/^(area|base|basefont|col|frame|hr|img|br|input|isindex|link|meta|param)$/.test(this.tagName.toLowerCase());   
    });  
} 
Gabe
  • 84,912
  • 12
  • 139
  • 238
sesame
  • 815
  • 1
  • 10
  • 18
1

How about something simple like this (not fully tested):

function outerHTML(node) {
    var el;
    if (node.outerHTML) {
        return node.outerHTML;
    } else if (node.parentNode && node.parentNode.nodeType == 1) {
        var el = document.createElement(node.parentNode.nodeName);
        el.appendChild( node.cloneNode(true) );
        return el.innerHTML;
    }
    return "";
}
Tim Down
  • 318,141
  • 75
  • 454
  • 536
1

Try:

(function(ele, html)
{if (typeof(ele.outerHTML)=='undefined')
    {var r=ele.ownerDocument.createRange();
     r.setStartBefore(ele);
     ele.parentNode.replaceChild(r.createContextualFragment(html), ele);
    }
 else
     {ele.outerHTML=html;
     }
})(aEle, aHtml);

for diyism

diyism
  • 12,477
  • 5
  • 46
  • 46
0

I know this is an old thread but if anyone finds this with Google (like I did) - I tried all these solutions and none of them worked out-of-the-box, since none handled both the getting and setting properties of outerHTML. I found this: which worked for me:

// Implement the outerHTML property for browsers that don't support it.
// Assumes that the browser does support innerHTML, has an extensible 
// Element.prototype, and allows getters and setters to be defined.
(function() {
// If we already have outerHTML return without doing anything
if (document.createElement("div").outerHTML) return;

// Return the outer HTML of the element referred to by this
function outerHTMLGetter() {
    var container = document.createElement("div"); // Dummy element
    container.appendChild(this.cloneNode(true));   // Copy this to dummy
    return container.innerHTML;                    // Return dummy content
}

// Set the outer HTML of the this element to the specified value
function outerHTMLSetter(value) {
    // Create a dummy element and set its content to the specified value
    var container = document.createElement("div");
    container.innerHTML = value;
    // Move each of the nodes from the dummy into the document
    while(container.firstChild)  // Loop until container has no more kids
        this.parentNode.insertBefore(container.firstChild, this);
    // And remove the node that has been replaced
    this.parentNode.removeChild(this);
}

// Now use these two functions as getters and setters for the 
// outerHTML property of all Element objects. Use ES5 Object.defineProperty
// if it exists and otherwise fall back on __defineGetter__ and Setter__.
if (Object.defineProperty) {
    Object.defineProperty(Element.prototype, "outerHTML", {
                              get: outerHTMLGetter,
                              set: outerHTMLSetter,
                              enumerable: false, configurable: true
                          });
}
else {
    Element.prototype.__defineGetter__("outerHTML", outerHTMLGetter);
    Element.prototype.__defineSetter__("outerHTML", outerHTMLSetter);
}
}());

Kudos: https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-15/implementing-the-outerhtml

JSP64
  • 1,454
  • 1
  • 16
  • 26
0

If all you want is the onclick attribute, then try the following: This assumes that you did not set the event using attachEvent or addEventListener.

elm.getAttribute("onclick");

If you want to make an outerHTML string (just promise not to take it apart after you make it):

function outerHTML(elm){
  var ret = "<"+elm.tagName;
  for(var i=0; i<elm.attributes.length; i++){
    var attr = elm.attributes[i];
    ret += " "+attr.name+"=\""+attr.nodeValue.replace(/"/, "\"")+"\"";
  }
  ret += ">";
  ret += elm.innerHTML+"</"+elm.tagName+">";
  return ret;
}

This function should do the trick in most cases, but it does not take namespaces into account.

Marius
  • 57,995
  • 32
  • 132
  • 151
  • Modify this by adding an if (attr) {...} and the undefined attributes will not be enumerated in the result. – Stan Rogers Sep 19 '10 at 15:01