Bug Tracker

Ticket #8142 (closed feature: wontfix)

Opened 4 years ago

Last modified 19 months ago

outerhtml

Reported by: rasmus@… Owned by: kswedberg
Priority: low Milestone: 1.7
Component: manipulation Version: 1.5
Keywords: Cc:
Blocking: Blocked by:

Description (last modified by ajpiano) (diff)

As pointed out in the thread below, the outerhtml() method seems to be a standard feature expected by many, and it's missing.

 http://stackoverflow.com/questions/2419749/jquery-get-selected-elements-outer-html

There's a solution for getting the outer HTML with jQuery posted in this thread - I'm not sure if it makes sense to provide a way to set the outer HTML, or how that would work.

Change History

comment:1 Changed 4 years ago by rwaldron

  • Owner set to rwaldron
  • Priority changed from undecided to low
  • Status changed from new to assigned
  • Component changed from unfiled to manipulation

In the spirit of jQuery.fn.width() having innerWidth and outerWidth variations, I think this is a good addition.

comment:2 Changed 4 years ago by jitter

I'm not convinced of the usefulness of this feature. Can you provide 2 or more use cases where this would be needed in "real life" applications.

Also the .outerHTML( newcontent ) is already available as .replaceWith( newcontent ).

And you should rarely be needing $(foo).outerHTML(), in most cases your probably want $(foo).clone()..... or $(foo).appendTo(...) or something like that.

Unless there is some really useful use case for this I'm missing, I guess we should stick to $(foo).eq(0).clone(true).wrap("<div></div>").html() or maybe provide this as a very small jQuery utility plugin

comment:3 Changed 4 years ago by mindplay

The use cases of outerhtml() are the same as for html(), only for cases where you want the markup of the container element itself. I'm sure I could provide many "real life" use cases where html() is useful, but I'm pretty sure none of those would be new to anyone.

One situation where outerhtml() would be preferable to html() is for cases where you need the markup of just one tag - typically end-nodes, like <img> tags for example. There's no direct way to do that at the moment.

Or in cases where you're dealing with multiple tags, for example a subset of list items, e.g. $('ul#users li.admin') which would include some of the children, but not all of them. In this case you can't simply clone and wrap, since you would be wrapping every individual child node.

I guess I would say, the value of this function is the same as most of the jQuery functions - most of them don't accomplish anything really fantastic that you couldn't do yourself with a few lines of code; the value is not having to write those few lines of code every time, and/or wondering which approach is most appropriate to accomplish what you want, with what you have.

In this case, you wouldn't need to worry or care whether you have one element or a list of elements - you just want the outer markup, as a string, without having to make decisions about which approach is best.

To me, that's the real value of jQuery - less time wasted on micro-management and trivial decision-making, since the jQuery functions handle most of the subtle variations of common tasks.

comment:4 Changed 4 years ago by jbasdf

I've needed an outerhtml() several times where the full string of the selected element was required. I've added a obj_to_s method to accomplish this, but it feels like it should be a part of jQuery. I add my vote to the feature.

comment:5 Changed 4 years ago by Justin Ball <justinball@…>

Something else to consider if/when creating an outerhtml() method. The current technique involves using append which results in jQuery parsing out all script tags to have them executed. I have a situation where I don't want the script tags executed. Rather I just want the string that includes the script tags. Below are the relevant comments from this discussion:  http://stackoverflow.com/questions/610995/jquery-cant-append-script-element

All of jQuery's insertion methods use a domManip function internally to clean/process elements before and after they are inserted into the DOM. One of the things the domManip function does is pull out any script elements about to be inserted and run them through an "evalScript routine" rather than inject them with the rest of the DOM fragment. It inserts the scripts separately, evaluates them, and then removes them from the DOM.

I believe that one of the reasons jQuery does this is to avoid "Permission Denied" errors that can occur in Internet Explorer when inserting scripts under certain circumstances. It also avoids repeatedly inserting/evaluating the same script (which could potentially cause problems) if it is within a containing element that you are inserting and then moving around the DOM.

comment:6 Changed 4 years ago by Justin Ball <justinball@…>

comment:7 Changed 4 years ago by FlipScript

I second (or third) the request for .outerHTML (or similar).

On my web site, I need to swap out a Flash movie for another. The <embed> tag has no .html() [meaning, no innerHTML], and even if it did, the Flash movie doesn't update dynamically if the parameter is modified once it is seated.

However, the movie can be swapped out in all browsers by getting the HTML for the entire tag, changing the movie parameter, and putting the new object back in the original objects place.

So, this use case would be:

  1. The need for the entire element contents (.outerHTML)
  2. Modifying the elements contents
  3. Doing a .replaceWith() on the original element with the changed contents

.replaceWith replaces the entire element, but actually getting the contents of the entire element is not entirely intuitive.

comment:8 Changed 3 years ago by jboesch

Here's a quick jsfiddle of what outerHTML might look like if it were added to jQuery:  http://jsfiddle.net/jboesch26/3SKsL/1/

comment:9 Changed 3 years ago by ajpiano

This comes up very regularly in IRC, a lot of people seem to have a need for it and there are several plugins that achieve this end. +1

Last edited 3 years ago by ajpiano (previous) (diff)

comment:10 follow-up: ↓ 11 Changed 3 years ago by john

  • Owner rwaldron deleted
  • Status changed from assigned to open

So while it seems that people expect this, I tend to agree with jitter - the potential for abuse is simply enormous. So many people don't understand the concept of a DOM Element and think that they have to have the HTML to inject an element some place else. If we add outerHTML we're going to see a ton of stuff like: $("ul").append( $("#thing").outerHTML() );

Let's discuss this in the 1.7 Roadmap meeting.

comment:11 in reply to: ↑ 10 Changed 3 years ago by ddebernardy@…

Gets my vote too. I've been bitten by this a few times as well, both with flash and table rows.

Replying to john:

(...) If we add outerHTML we're going to see a ton of stuff like: $("ul").append( $("#thing").outerHTML() );

Isn't it already possible to do this?

<div id="thing">
<li id="in-thing">blah...</li>
</div>
... 
$("ul").append( $("#thing").html() );

comment:12 Changed 3 years ago by timmywil

I'm also with jitter. In response to the last comment, yes that is possible, but $("ul").append( $("#thing") ) would work just fine without calling outerHTML(), hence the confusion.

comment:13 Changed 3 years ago by john

  • Keywords 1.7-discuss added

Nominating ticket for 1.7 discussion.

comment:14 Changed 3 years ago by rwaldron

  • Description modified (diff)

-1,

comment:15 Changed 3 years ago by jaubourg

-1, Official plugin with dom manipulation/selection helpers

comment:16 Changed 3 years ago by timmywil

+1, This is done too often. outerHTML could be used if available and give the user a performance boost over always appending to a disconnected node and calling html.

comment:17 Changed 3 years ago by dmethvin

  • Description modified (diff)

+0, Not convinced it's needed, and not sure of the cost/effectiveness of an internal implementation. The clone/wrap seems like it could cause trouble with table component elements

comment:18 Changed 3 years ago by mike@…

This would be useful. My use case is I am duplicating a table row and inserting it into a table at a specified row index. I guess I will have to wrap it in a div using clonedRow.wrap("<div></div>").html(). Even though I am cloning an element anyway, this is still not an obvious way to get the outerHTML, which is a standard thing you would expect jQuery would provide.

In some case you don't want to clone the element for perf reasons and it would be good to have a nicely optimised and cross browser way of doing this - exactly where you would hope jQuery would be able to help.

comment:19 Changed 3 years ago by john

  • Description modified (diff)

-1, I fear that this will be abused by people not understanding what a DOM node is.

comment:20 Changed 3 years ago by ajpiano

  • Description modified (diff)

+1, but I think maybe we should make it a getter but not a setter - that seems to be John's main reservation - but I've never seen anyone who's WANTED to use it as a setter...they just want an HTML string.

comment:21 Changed 3 years ago by flori

+1 It took me a while to work around this. I ended up with the following workaround:

$("<div/>").append(select).html()

"select" was an native HTML select element, I needed it with attributes.

A clean .outerHtml() would make code a lot more readable.

comment:22 Changed 3 years ago by rwaldron

  • Keywords needsdocs added; 1.7-discuss removed
  • Milestone changed from 1.next to 1.7

Will publish a page that explains why outerHTML was not accepted into 1.7

comment:23 Changed 3 years ago by HumanWannabe

I understand that this may actually be an "abuse" case, but I would love an "outerHTML" (only get) for testing purposes. It is common when you have problems in your script to want to check which element causes it. You can of course do this with workarounds (for example add IDs to all your elements and print these) but having the ability to directly print the HTML of the element would be very useful.

comment:24 Changed 3 years ago by rwaldron

  • Owner set to kswedberg
  • Status changed from open to assigned

comment:25 Changed 3 years ago by Kyle Adams <kyle@…>

Chalk me up as someone who's run into real life situations where I needed it. I'm also fine with it being a getter only. Here are the two situations I've run into recently on Sourceforge.net:

Case #1: we log information about various parts of the page - did it load, what loaded, how long did it take to load, etc. We frequently need to do something like $('#dynamic-div a').outerhtml() to capture all the HTML we want to log.

Case #2: On our download page we initiate a five second countdown before beginning the download. The initial text reads something like <div id="download_text">Your <a href="/projects/myproject">My Project</a> download will begin shortly.". When the countdown begins, we replace the text with "Your {project} download will start in {sn} seconds...". The new string is a template that gets passed into the jQuery countdown plugin we're using. Before we pass it into the plugin we need to plunk $('#download_text a').outerhtml() in place of {project}.

If there's a non-outerHTML solution and/or if I'm abusing the DOM, I'd love to know.

comment:26 Changed 3 years ago by tjerk.meesters@…

Another proposal, without using .clone() and supporting both get and set; i've seen the setter being used in Flash players, such as JW.

Basically I just enumerate the attributes of the main element in browsers that don't support outerHTML, since I figured that's the least intrusive operation. It does produce a bit more code though :-/

 http://jsfiddle.net/pZHHm/

comment:27 Changed 3 years ago by dmethvin

  • Status changed from assigned to closed
  • Resolution set to wontfix

Discussed in the 1.7 meeting and voted out due to the issues discussed above. I suppose the docs should add a sentence in the .html() method indicating that operations similar to outerHTML can be done using .wrap() or .replaceWith() for example.

comment:28 follow-up: ↓ 32 Changed 2 years ago by anonymous

There are over 25 "likes" on a comment in the documentation for .html() that says:

html() returns the content so whenever I need the whole element, I have to:

wrap("<div>").parent().html()

Isn't it about time we got outerHtml() or something like that?</div>

You've got numerous posters in this bug, all requesting an outerHTML. You have other duplicate bugs, similarly requesting it.

You have a Stack Overflow post on the issue ( http://stackoverflow.com/questions/2419749/get-selected-elements-outer-html) with over a hundred upvotes.

An outerHTML method would be easy to implement (wrap+parent+html) and would make very many jQuery users very happy. So my question is ... why all the hate on this feature? Everyone wants it, and the only argument against it amounts to "well if some idiot does something idiotic then he's going to get some idiotic results".

Idiot devs are going to idiotic things regardless of what jQuery does or doesn't add to its library. Please, please, stop penalizing every non-idiotic jQuery user for the (purely hypothetical) sins of the idiotic ones ... and add a .outerHtml().

comment:29 Changed 2 years ago by mindplay

Would it make more sense to have the html() method itself support this feature? e.g.:

html() -> inner HTML (existing)
html(val) -> replace inner HTML (existing)
html(true) -> outer HTML
html(val, true) -> replace outer HTML

comment:30 Changed 2 years ago by gcpantazis

Our solution was simply this:

 https://gist.github.com/2871256

But if the concern is simply to prevent inexperienced developers from doing something stupid (as mentioned above), I think what mindplay suggests is a good idea; make outerHTML an optional second parameter of .html(); you'd have to go out of your way (read: want that to happen) to make it happen.

comment:31 Changed 2 years ago by rwaldron

#11858 is a duplicate of this ticket.

comment:32 in reply to: ↑ 28 Changed 2 years ago by gnarf

Replying to anonymous:

You have a Stack Overflow post on the issue ( http://stackoverflow.com/questions/2419749/get-selected-elements-outer-html) with over a hundred upvotes.

An outerHTML method would be easy to implement (wrap+parent+html) and would make very many jQuery users very happy. So my question is ... why all the hate on this feature? Everyone wants it, and the only argument against it amounts to "well if some idiot does something idiotic then he's going to get some idiotic results".

Idiot devs are going to idiotic things regardless of what jQuery does or doesn't add to its library. Please, please, stop penalizing every non-idiotic jQuery user for the (purely hypothetical) sins of the idiotic ones ... and add a .outerHtml().

And a plugin can solve this issue, after all it is as simple as:

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

as that 117 vote question from stack overflow gracefully shows.

---

Personally, I'm +0 on this one, since the implementation is pretty tiny, but I understand the -1 and the +1 view points.

-1: .outerHtml is something that isn't difficult to do without core support, it can easily be implemented in a plugin as shown. If jQuery Core adds it, we need to start supporting it (and by adding it, we encourage its use)

+1: Lots of people do want to do this, and most people just expect it to work.

Last edited 2 years ago by gnarf (previous) (diff)

comment:33 Changed 2 years ago by mikesherov

This is a classic example of "isn't it just as simple as..."? Theoretically, yes. However, jQuery is a public API. When new methods are released, that is a specific endorsement of an idea, and along with that comes support. Support means tests, tests mean bugs, bugs mean edge cases, edge cases mean hours of debug. It's never as simple as it seems.

Also, the criticism that the reasons against implementing amount to "well if some idiot does something idiotic then he's going to get some idiotic results" is unfair. Here is a summary of the reasons given, plus one of my own:

  1. easy to implement as a plugin
  2. hazy interactions with tables (which is exactly what a few people are trying to use this for).
  3. similar well-tested API methods do almost the exact same thing
  4. how does this method handle data or events associated with the element being replaced?

Overall, I'm +0 on this feature. However, I just wanted to point out that the reasons given for not implementing were thoughtfully considered, and the pros and cons weighed. We should continue the discussion, but let's not be reductive here.

comment:34 Changed 2 years ago by dmethvin

Right on @mikesherov. This is easy to do if we ignore consistency and the details. And if we do that, there will definitely be plenty of tickets arriving here in the tracker to tell us how .outerHTML() is broken for their case.

If the simple implementation above suits *your* needs, there is no problem at all with you using it.

comment:35 Changed 2 years ago by mindplay

I'm +0 at this point, but I would add this: with apparently hundreds of developers implementing their own version of this, we do have a consistency issue - of these hundreds of different implementations, my guess is maybe 50 of those are going to be largely identical.

Consistent DOM manipulation - sounds like half the reason jQuery exists in the first place...

comment:36 Changed 2 years ago by anonymous

+1 to adding the outerHTML method. Yes - it can be abused, but avoiding bad development practices can't be helped by limiting the number of options available to the developers.

comment:37 Changed 23 months ago by mikesherov

  • Keywords needsdocs removed

comment:38 Changed 21 months ago by zuallauz

We need an outerHtml() function. There's the $('#element').html() function which is essentially an inner HTML function which copies all the html inside the #element tag. But why can't we copy the whole #element tag including its id, classes, attributes, events and its html contents easily?

For example, how do you copy the exact HTML of an img tag including all of its attributes? Ok lets say you go up one level to the parent element then do a .html() on that. Ok that will work for this case. But lets say you have other HTML or img tags inside that parent that you don't want to copy. What if we just want the entire HTML of one img tag including its id, classes, attributes, events etc?

According to  this StackOverflow post we need to do something like this:

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

This basically takes a copy of the entire #imgElement into memory, then we wrap it in paragraph tags, then we go up a level to the paragraph tag and get the inner HTML on that to get the entire #element tag's HTML. It's a clever solution to the problem, but lets be honest, it's a kludge. We shouldn't need to do a big line like that. Everyone needing an outerHtml() function has to do something like this because it's not a core jQuery function.

Now honestly how much extra work is it going to create in bug tickets/support by adding a small utility function to get the outer HTML? Not much if any. All you need is a simple explanation of what .html() does and what .outerHtml() does. For example the HTML:

<p class="person" data-name="Bob Brown">
   <span>abc 123</span>
</p>

.html() returns:

<span>abc 123</span>

.outerHtml() returns:

<p class="person" data-name="Bob Brown">
   <span>abc 123</span>
</p>

So easy. In short no-one is going to get confused by that and you're not suddenly going to get an influx of bug requests. Also the code to do this is only going to increase the size of jQuery by a few bytes. I dislike the idea of it being a plugin, because then no-one knows about it and something this useful and fundamental should really be a core feature.

+1 Please re-open this ticket and vote on for jQuery 1.9!

Last edited 21 months ago by zuallauz (previous) (diff)

comment:39 Changed 21 months ago by dmethvin

We need an outerHtml() function.

You just posted one. Use it. That doesn't have to go into jQuery itself for you to use it, just add that line to your own code if it suits your needs and you understand its limitations.

comment:40 Changed 21 months ago by anonymous

Isn't the whole .clone().wrap('<p>').parent().html() way less efficient than [0].outerHTML, when outerHTML is available?

FWIW, +1 from me.

comment:41 Changed 20 months ago by anonymous

What I wanted to do (quite many times) is replace the whole -- outdated -- content of a container element (div) with some new html content, like this:

var $container=$('#container');
var $span = $('<span>').addClass('hot').append('fresh news');

$container.html($span[0].outerHTML);

what I finally did was:

$container.html('').append($span);

hope this helps anyone.

comment:42 Changed 20 months ago by anonymous

sorry, just realized there's an .empty() for the .html() above:

$container.empty().append($span);

comment:43 Changed 19 months ago by Bill

For what it's worth, here's another use case:

I have a list of links that exist as DOM nodes and I want to render them in a comma delimited list. Essentially, I'd like to do this:

listoflinks.append(link.outerHtml());
jQuery('<td>').append(listoflinks.join())

This would be useful for any kind of mixed content manipulation.

comment:44 Changed 19 months ago by Tomas Sandven <tomas191191@…>

Use case:

While developing a JavaScript application I have my controllers set to reload templates using Ajax and re-render them every 2 seconds or so. I would only like to update the HTML if the template or contents has changed though -something like this would be nice:

var html = template.render('header', {'size': this.size});

if(html != this.el.outerHtml()) {
    this.el.replace($(html));
}

comment:45 Changed 19 months ago by dmethvin

I's a pretty wasteful design, sending the entire unchanged data set from the server and re-rendering it, then throwing it away if it turns out you rendered the same thing last time.

So the lack of .outerHtml() is the least of your worries, but if you really need it:  http://plugins.jquery.com/outerHtml/

Note: See TracTickets for help on using tickets.