ChatGPT解决这个技术问题 Extra ChatGPT

Get selected element's outer HTML

I'm trying to get the HTML of a selected object with jQuery. I am aware of the .html() function; the issue is that I need the HTML including the selected object (a table row in this case, where .html() only returns the cells inside the row).

I've searched around and found a few very ‘hackish’ type methods of cloning an object, adding it to a newly created div, etc, etc, but this seems really dirty. Is there any better way, or does the new version of jQuery (1.4.2) offer any kind of outerHtml functionality?

It is outrageous that jQuery does not have means to do such a thing. I need this too.
I've posted a feature request, with a reference to this thread, and the initial response was positive. bugs.jquery.com/ticket/8142
To save some people a few seconds of their time from trying out Ulhas Tuscano's solution, it doesn't work.
Uh, wth is going on. $('div')[0].outerHTML.
@Tuscan meant $("#selectorid").prop("outerHTML")

P
Protector one

I believe that currently (5/1/2012), all major browsers support the outerHTML function. It seems to me that this snippet is sufficient. I personally would choose to memorize this:

// Gives you the DOM element without the outside wrapper you want
$('.classSelector').html()

// Gives you the outside wrapper as well only for the first element
$('.classSelector')[0].outerHTML

// Gives you the outer HTML for all the selected elements
var html = '';
$('.classSelector').each(function () {
    html += this.outerHTML;
});

//Or if you need a one liner for the previous code
$('.classSelector').get().map(function(v){return v.outerHTML}).join('');

EDIT: Basic support stats for element.outerHTML

Firefox (Gecko): 11 ....Released 2012-03-13

Chrome: 0.2 ...............Released 2008-09-02

Internet Explorer 4.0...Released 1997

Opera 7 ......................Released 2003-01-28

Safari 1.3 ...................Released 2006-01-12


@SalmanPK FireFox didn't support this property until 2011-11-11. bugzilla.mozilla.org/show_bug.cgi?id=92264 There are still a lot of users stuck on 3.6. I think this is actually a perfect example of why one would chose to use jQuery over native functionality.
@LuciferSam Firefox 3.6 has ~6% global market share according to gs.statcounter.com However, filtering the results to the last 6 months (Dec '11-May '12) and to USA pushes it off their top 12 list (below 3%). I chose this window since this article suggests FF 3.6 usage dropped significantly after Jan 2012. Given this data, I stand by my solution for simpler code over backwards compatibility.
Couldn't agree more. This is the right answer here, not the other stuff people are suggesting. The element I select on has attributes I want to keep which are lost by the other answers. Hell, this even works in IE!
No. Firefox 11 was not released until March 13, 2012 (fixed now), i.e. less than a year ago as of this writing. One of the benefits of jQuery is that it supports older browsers. I think supporting at least a year is reasonable, and some sites are obviously going to want more (remember, jQuery supports IE6).
@EricHu statcounter also states IE8 has 9.3% of the global browser share. Yet some of my websites are close to the 40% mark. It's all relative and varies enormously from website to website, Firefox 3.6 still accounts for roughly 10% on some of my websites. Global marketshare means nothing. It's all about your websites audience.
p
philipvr

No need to generate a function for it. Just do it like this:

$('a').each(function(){
    var s = $(this).clone().wrap('<p>').parent().html();
    console.log(s);
});

(Your browser's console will show what is logged, by the way. Most of the latest browsers since around 2009 have this feature.)

The magic is this on the end:

.clone().wrap('<p>').parent().html();

The clone means you're not actually disturbing the DOM. Run it without it and you'll see p tags inserted before/after all hyperlinks (in this example), which is undesirable. So, yes, use .clone().

The way it works is that it takes each a tag, makes a clone of it in RAM, wraps with p tags, gets the parent of it (meaning the p tag), and then gets the innerHTML property of it.

EDIT: Took advice and changed div tags to p tags because it's less typing and works the same.


I wonder why the jQuery team doesn't add a outerHtml() method?
@Derek, that wouldn't matter. I'm using DIV as a wrapper in order to get something inside it.
.clone().wrap('

').parent().html(); is shorter

Yes, less keystrokes, and achieves the same effect.
Better to use DIV instead of P for a general solution -- not all elements can be wrapped in P as valid HTML.
D
David V.

2014 Edit : The question and this reply are from 2010. At the time, no better solution was widely available. Now, many of the other replies are better : Eric Hu's, or Re Capcha's for example.

This site seems to have a solution for you : jQuery: outerHTML | Yelotofu

jQuery.fn.outerHTML = function(s) {
    return s
        ? this.before(s).remove()
        : jQuery("<p>").append(this.eq(0).clone()).html();
};

I saw this but was trying to avoid it because it seemed to hackish and like there should have been a better way, but it works well. Thanks.
$('[selector]')[0].outerHTML
@drogon: Be aware that outerHTML is supported in Firefox only since version 11 (March 2012).
@PavingWays: In Firefox' defense: outerHTML is a proprietary attribute invented by Microsoft, not a W3C standard. (Fun fact: innerHTML is standardized only since HTML5)
pure js el.outerHTML || document.createElement('div').appendChild( el.cloneNode( true ) ).parentNode.innerHTML
R
Re Captcha

What about: prop('outerHTML')?

var outerHTML_text = $('#item-to-be-selected').prop('outerHTML');

And to set:

$('#item-to-be-selected').prop('outerHTML', outerHTML_text);

It worked for me.

PS: This is added in jQuery 1.6.


Very neat and small code compared to the other answers. Q: Does this have the same outerHTML restrictions as noted in other answers? Does it work in FF < 11?
Works well, this might just be the best solution here. As far as browsers go this should be as compatible as outerHTLM. The prop() method is basically just getting the outerHTML property.
This solution is better, however Jquery 1.6.1 was released in 2011. The question (and my reply) are from 2010.
For me "$('[selector]')[0].outerHTML;" did not work in any case, but "$('#item-to-be-selected').prop('outerHTML');" yes!
$('#item-to-be-selected').attr('outerHTML'); //** For earlier jQuery's
G
Greeso

Extend jQuery:

(function($) {
  $.fn.outerHTML = function() {
    return $(this).clone().wrap('<div></div>').parent().html();
  };
})(jQuery);

And use it like this: $("#myTableRow").outerHTML();


Small problem with this solution: you need to clone() before you wrap(), otherwise you're leaving the extra wrapping
in the document.
Thanks, mindplay.dk -- I edited the code to incorporate your suggestion.. good catch. :)
What about reversing that to be: return $('<div>').append(this.clone()).html(); It's just a little more to-the-point.
You should check for outerHTML first and just use that for the browsers that support it
This is great for people like me who like to build HTML in AJAX responses using jQuery objects, and the .appendTo() and .append() functions. .outerHTML doesn't work for those instances from what I saw in my testing. Someone else might want to check that out more, but I don't have time.
T
Tokimon

I agree with Arpan (Dec 13 '10 5:59).

His way of doing it is actually a MUCH better way of doing it, as you dont use clone. The clone method is very time consuming, if you have child elements, and nobody else seemed to care that IE actually HAVE the outerHTML attribute (yes IE actually have SOME useful tricks up its sleeve).

But I would probably create his script a bit different:

$.fn.outerHTML = function() {
    var $t = $(this);
    if ($t[0].outerHTML !== undefined) {
        return $t[0].outerHTML;
    } else {
        var content = $t.wrap('<div/>').parent().html();
        $t.unwrap();
        return content;
    }
};

This worked perfectly for me. Due to a bug with clone() and textarea's, I needed a non-clone solution, and this was spot on.
+1 for using native outerHTML where available, since it's supported by Chrome in addition to Internet Explorer.
if (!('outerHTML' in $t[0])) alert('god dammit, update your browser');
B
Brian Donovan

To be truly jQuery-esque, you might want outerHTML() to be a getter and a setter and have its behaviour as similar to html() as possible:

$.fn.outerHTML = function (arg) {
    var ret;

    // If no items in the collection, return
    if (!this.length)
        return typeof arg == "undefined" ? this : null;
    // Getter overload (no argument passed)
    if (!arg) {
        return this[0].outerHTML || 
            (ret = this.wrap('<div>').parent().html(), this.unwrap(), ret);
    }
    // Setter overload
    $.each(this, function (i, el) {
        var fnRet, 
            pass = el,
            inOrOut = el.outerHTML ? "outerHTML" : "innerHTML";

        if (!el.outerHTML)
            el = $(el).wrap('<div>').parent()[0];

        if (jQuery.isFunction(arg)) { 
            if ((fnRet = arg.call(pass, i, el[inOrOut])) !== false)
                el[inOrOut] = fnRet;
        }
        else
            el[inOrOut] = arg;

        if (!el.outerHTML)
            $(el).children().unwrap();
    });

    return this;
}

Working demo: http://jsfiddle.net/AndyE/WLKAa/

This allows us to pass an argument to outerHTML, which can be

a cancellable function — function (index, oldOuterHTML) { } — where the return value will become the new HTML for the element (unless false is returned).

a string, which will be set in place of the HTML of each element.

For more information, see the jQuery docs for html().


This should be added to jQuery core so that people don't need to think about it. My only question is whether wrap()/unwrap() is likely to be better or worse performance than clone()?
IMSoP: Generally, wrap/unwrap will be faster because clone has to copy all child elements and attributes of the element. wrap() only creates a single element, and unwrap() destroys it, all the other elements are just moved, which is a fairly quick operation most of the time.
T
Tofandel

You can also use get (Retrieve the DOM elements matched by the jQuery object.).

e.g:

$('div').get(0).outerHTML;//return "<div></div>"

As extension method :

jQuery.fn.outerHTML = function () {
  return this.get().map(function (v) {
    return v.outerHTML
  }).join()
};

Or

jQuery.fn.outerHTML = function () {
  return $.map(this.get(), function (v) {
    return v.outerHTML
  }).join()
};

Multiple choice and return the outer html of all matched elements.

$('input').outerHTML()

return:

'<input id="input1" type="text"><input id="input2" type="text">'

S
SpYk3HH

To make a FULL jQuery plugin as .outerHTML, add the following script to any js file and include after jQuery in your header:

update New version has better control as well as a more jQuery Selector friendly service! :)

;(function($) {
    $.extend({
        outerHTML: function() {
            var $ele = arguments[0],
                args = Array.prototype.slice.call(arguments, 1)
            if ($ele && !($ele instanceof jQuery) && (typeof $ele == 'string' || $ele instanceof HTMLCollection || $ele instanceof Array)) $ele = $($ele);
            if ($ele.length) {
                if ($ele.length == 1) return $ele[0].outerHTML;
                else return $.map($("div"), function(ele,i) { return ele.outerHTML; });
            }
            throw new Error("Invalid Selector");
        }
    })
    $.fn.extend({
        outerHTML: function() {
            var args = [this];
            if (arguments.length) for (x in arguments) args.push(arguments[x]);
            return $.outerHTML.apply($, args);
        }
    });
})(jQuery);

This will allow you to not only get the outerHTML of one element, but even get an Array return of multiple elements at once! and can be used in both jQuery standard styles as such:

$.outerHTML($("#eleID")); // will return outerHTML of that element and is 
// same as
$("#eleID").outerHTML();
// or
$.outerHTML("#eleID");
// or
$.outerHTML(document.getElementById("eleID"));

For multiple elements

$("#firstEle, .someElesByClassname, tag").outerHTML();

Snippet Examples:

console.log('$.outerHTML($("#eleID"))'+"\t", $.outerHTML($("#eleID"))); console.log('$("#eleID").outerHTML()'+"\t\t", $("#eleID").outerHTML()); console.log('$("#firstEle, .someElesByClassname, tag").outerHTML()'+"\t", $("#firstEle, .someElesByClassname, tag").outerHTML()); var checkThisOut = $("div").outerHTML(); console.log('var checkThisOut = $("div").outerHTML();'+"\t\t", checkThisOut); $.each(checkThisOut, function(i, str){ $("div").eq(i).text("My outerHTML Was: " + str); });

This will
be Replaced
At RunTime

Open Console to see results


p
pbfy0

you can also just do it this way

document.getElementById(id).outerHTML

where id is the id of the element that you are looking for


$("#" + id).attr("id") is incredibly redundant. If you already have the id in a variable, why are you using a jquery selector to pull up the element from the dom, then querying its ID attribute?
A
Arpan Dhandhania

I used Jessica's solution (which was edited by Josh) to get outerHTML to work on Firefox. The problem however is that my code was breaking because her solution wrapped the element into a DIV. Adding one more line of code solved that problem.

The following code gives you the outerHTML leaving the DOM tree unchanged.

$jq.fn.outerHTML = function() {
    if ($jq(this).attr('outerHTML'))
        return $jq(this).attr('outerHTML');
    else
    {
    var content = $jq(this).wrap('<div></div>').parent().html();
        $jq(this).unwrap();
        return content;
    }
}

And use it like this: $("#myDiv").outerHTML();

Hope someone finds it useful!


Just use .clone like @mindplay suggests in his comment- it's easier
A
Abdennour TOUMI
// no cloning necessary    
var x = $('#xxx').wrapAll('<div></div>').parent().html(); 
alert(x);

Fiddle here: http://jsfiddle.net/ezmilhouse/Mv76a/


M
Mark Schultheiss

If the scenario is appending a new row dynamically, you can use this:

var row = $(".myRow").last().clone();
$(".myRow").last().after(row);

.myrow is the classname of the <tr>. It makes a copy of the last row and inserts that as a new last row. This also works in IE7, while the [0].outerHTML method does not allow assignments in ie7


C
Community

node.cloneNode() hardly seems like a hack. You can clone the node and append it to any desired parent element, and also manipulate it by manipulating individual properties, rather than having to e.g. run regular expressions on it, or add it in to the DOM, then manipulate it afterwords.

That said, you could also iterate over the attributes of the element to construct an HTML string representation of it. It seems likely this is how any outerHTML function would be implemented were jQuery to add one.


I
Ivan Chaer

I've used Volomike's solution updated by Jessica. Just added a check to see if the element exists, and made it return blank in case it doesn't.

jQuery.fn.outerHTML = function() {
return $(this).length > 0 ? $(this).clone().wrap('<div />').parent().html() : '';
};

Of course, use it like:

$('table#buttons').outerHTML();

D
Darlesson

You can find a good .outerHTML() option here https://github.com/darlesson/jquery-outerhtml.

Unlike .html() that returns only the element's HTML content, this version of .outerHTML() returns the selected element and its HTML content or replaces it as .replaceWith() method but with the difference that allows the replacing HTML to be inherit by the chaining.

Examples can also be seeing in the URL above.


H
Holly

This is quite simple with vanilla JavaScript...

document.querySelector('#selector')

m
mindplay.dk

Note that Josh's solution only works for a single element.

Arguably, "outer" HTML only really makes sense when you have a single element, but there are situations where it makes sense to take a list of HTML elements and turn them into markup.

Extending Josh's solution, this one will handle multiple elements:

(function($) {
  $.fn.outerHTML = function() {
    var $this = $(this);
    if ($this.length>1)
      return $.map($this, function(el){ return $(el).outerHTML(); }).join('');
    return $this.clone().wrap('<div/>').parent().html();
  }
})(jQuery);

Edit: another problem with Josh's solution fixed, see comment above.


Most jQuery "getter" methods return data for the first element only, so it would make more sense to match this behaviour.
I think I stated clearly why it works this way? It would make for ugly/complicated code when you have a list of elements - if for some reason you want the markup for only the first element, just use :first in your selector.
Sure, just like you could just use map with everyone else's solution to get the HTML of multiple elements. All I was saying is that it's more consistent to match the behaviour of the standard jQuery methods.
C
Community

Anothe similar solution with added remove() of the temporary DOM object.


P
Petar

I have made this simple test with outerHTML being tokimon solution (without clone), and outerHTML2 being jessica solution (clone)

console.time("outerHTML");
for(i=0;i<1000;i++)
 {                 
  var html = $("<span style='padding:50px; margin:50px; display:block'><input type='text' title='test' /></span>").outerHTML();
 }                 
console.timeEnd("outerHTML");

console.time("outerHTML2");

 for(i=0;i<1000;i++)
 {                 
   var html = $("<span style='padding:50px; margin:50px; display:block'><input type='text' title='test' /></span>").outerHTML2();
  }                 
  console.timeEnd("outerHTML2");

and the result in my chromium (Version 20.0.1132.57 (0)) browser was

outerHTML: 81ms outerHTML2: 439ms

but if we use tokimon solution without the native outerHTML function (which is now supported in probably almost every browser)

we get

outerHTML: 594ms outerHTML2: 332ms

and there are gonna be more loops and elements in real world examples, so the perfect combination would be

$.fn.outerHTML = function() 
{
  $t = $(this);
  if( "outerHTML" in $t[0] ) return $t[0].outerHTML; 
  else return $t.clone().wrap('<p>').parent().html(); 
}

so clone method is actually faster than wrap/unwrap method (jquery 1.7.2)


g
gtournie

Here is a very optimized outerHTML plugin for jquery: (http://jsperf.com/outerhtml-vs-jquery-clone-hack/5 => the 2 others fast code snippets are not compatible with some browsers like FF < 11)

(function($) {

  var DIV = document.createElement("div"),
      outerHTML;

  if ('outerHTML' in DIV) {
    outerHTML = function(node) {
      return node.outerHTML;
    };
  } else {
    outerHTML = function(node) {
      var div = DIV.cloneNode();
      div.appendChild(node.cloneNode(true));
      return div.innerHTML;
    };
  }

  $.fn.outerHTML = function() {
    return this.length ? outerHTML(this[0]) : void(0);
  };

})(jQuery);

@Andy E => I don't agree with you. outerHMTL doesn't need a getter AND a setter: jQuery already give us 'replaceWith'...

@mindplay => Why are you joining all outerHTML? jquery.html return only the HTML content of the FIRST element.

(Sorry, don't have enough reputation to write comments)


r
rolacja

Short and sweet.

[].reduce($('.x'), function(i,v) {return i+v.outerHTML}, '')

or event more sweet with help of arrow functions

[].reduce.call($('.x'), (i,v) => i+v.outerHTML, '')

or without jQuery at all

[].reduce.call(document.querySelectorAll('.x'), (i,v) => i+v.outerHTML, '')

or if you don't like this approach, check that

$('.x').get().reduce((i,v) => i+v.outerHTML, '')

S
Somnath Muluk

This is great for changing elements on the dom but does NOT work for ie when passing in a html string into jquery like this:

$('<div id="foo">Some <span id="blog">content</span></div>').find('#blog').outerHTML();

After some manipulation I have created a function which allows the above to work in ie for html strings:

$.fn.htmlStringOuterHTML = function() {     
    this.parent().find(this).wrap('<div/>');        
    return this.parent().html();
};

e
evandrix

$.html = el => $("<div>"+el+"</div>").html().trim();


d
dav1d

I came across this while looking for an answer to my issue which was that I was trying to remove a table row then add it back in at the bottom of the table (because I was dynamically creating data rows but wanted to show an 'Add New Record' type row at the bottom).

I had the same issue, in that it was returning the innerHtml so was missing the TR tags, which held the ID of that row and meant it was impossible to repeat the procedure.

The answer I found was that the jquery remove() function actually returns the element, that it removes, as an object. So, to remove and re-add a row it was as simple as this...

var a = $("#trRowToRemove").remove();            
$('#tblMyTable').append(a);  

If you're not removing the object but want to copy it somewhere else, use the clone() function instead.


S
Sky Yip

jQuery plugin as a shorthand to directly get the whole element HTML:

jQuery.fn.outerHTML = function () {
    return jQuery('<div />').append(this.eq(0).clone()).html();
};

And use it like this: $(".element").outerHTML();


R
Rehmat

Pure JavaScript:

var outerHTML = function(node) {
  var div = document.createElement("div");
  div.appendChild(node.cloneNode(true));
  return div.innerHTML;
};

v
vapcguy
$("#myNode").parent(x).html(); 

Where 'x' is the node number, beginning with 0 as the first one, should get the right node you want, if you're trying to get a specific one. If you have child nodes, you should really be putting an ID on the one you want, though, to just zero in on that one. Using that methodology and no 'x' worked fine for me.


People voting this down, care to leave a comment? I'm having the code go UP to the PARENT, then get the HTML of the contents - not just doing a .html() on the given element. THIS WORKS and I'd like an explanation of why it doesn't.
Z
Zeeshan

Simple solution.

var myself = $('#div').children().parent();

b
brettkelly
$("#myTable").parent().html();

Perhaps I'm not understanding your question properly, but this will get the selected element's parent element's html.

Is that what you're after?


Acually no, because if that parent has other children he'll get that html too.
...what he said. I'm looking for the element itself, not it and all its parent's other children. How did this get two up votes???