1

I'm looking for a native/jQuery method to return an elements opening tag with all its attributes and their values as a string.

It should look something like this.

HTML:

<body id="mybody" class="someclass">

</body>

Non-existing jQuery function:

$("body").tagAsString(); //returns <body id="mybody" class="someclass">

or non-existing native function:

document.getElementsByTagName("body")[0].tagToString();

I can get all attributes of an element with their values, but I need the exact string as it is just in the HTML. Looking for the length of the whole opening tag.

Milanzor
  • 1,920
  • 14
  • 22
  • 2
    You likely won't be able to get it exactly as it is in the html due to how the browser interprets html, however you can get the browsers' interpretation of it using .outerHTML on the dom node. – Kevin B Oct 31 '13 at 20:40
  • 3
    Is there a reason you need this? It's an odd requirement, which sounds like a bad solution to a different problem. – Rory McCrossan Oct 31 '13 at 20:40
  • I'm replacing the content with the content from another page retrieved by ajax, but I don't always have control over the other page. I'm currently getting all content in the retrieved body by using data.substring(data.indexOf("") + 6, data.indexOf(""). if the retrieved data has more then 6 characters in the body tag, my script will fail, so I need the length – Milanzor Oct 31 '13 at 20:44
  • Have you tried something like `$(data).find('body')` and working with that? – Jason P Oct 31 '13 at 20:48
  • 1
    @Milfert definitely an XY problem then - just do `$(document.body).empty().append($(data).children())` – Alnitak Oct 31 '13 at 20:48
  • Thought I had the right way of approaching so I asked the question like this. Appears I didn't and need another approach. Cheers for the help so far! – Milanzor Oct 31 '13 at 21:07

4 Answers4

4

The problem is that, once the HTML has been parsed, the browser doesn't care about it any more. The DOM structure that the browser has interpreted is all that matters. The HTML is gone. Kaput. Finished. Ignored.

How the browser thinks of the HTML after parsing it.

The browser does not keep the "original HTML" in any way. In fact, the browser doesn't really like HTML. It's not how it thinks, and converting things to HTML can be a bit tricky.

All you can get is a representation of how the browser currently has the element. This may well not be the same as the HTML. For instance, any attributes that have been changed in the DOM will appear differently to the original HTML.

The process might look like this:

var html = $(document.body)
    .clone() // get a copy of the element to work on
    .empty() // empty it
    .appendTo('<div/>').parent().html(); // equivalent to outerHTML

This gives a result like:

'<body class="question-page"></body>'

You could then remove everything from the final <:

html = html.substr(0,html.lastIndexOf('<'));

Which gives:

'<body class="question-page">'
lonesomeday
  • 233,373
  • 50
  • 316
  • 318
  • After all the testing and researching I would say this actually is the real answer to my **original** question. Alnitak showed me that, in fact, I asked the wrong question. I'm marking this as the accepted answer, but note that your code appends $(document.body) into a div, while the is not a valid child of a
    , so the actual element does not get appended into the
    , it's contents does though. **Using any other element than the doctype, html, head and body tags will make this work**.
    – Milanzor Nov 01 '13 at 21:42
  • @Milanzor Hence why you clone the `body` element. It's perfectly fine to have a `div` contain a `body`, so long as they aren't actually in the document. This technique wouldn't work otherwise. – lonesomeday Nov 01 '13 at 21:54
  • ah, that's good to hear. Thanks for clarifying that. Unfortunatly I cannot clone the body as its just part of a string and parsing the string to jQuery e.g. $(string); removes the body element. – Milanzor Nov 01 '13 at 22:00
  • Love the intro and the image. Wonderful pedagogy and great explanation! – Prid Aug 20 '19 at 06:15
2

Given the information just supplied in the comments describing the actual problem you're trying to solve, you don't need to retrieve the body tag's attributes at all, and nor should you be using string searching. Just do:

var $newContent = $('<div>' + data + '</div>').children();
$(document.body).empty().append($newContent);

i.e. parse the entire data - $(data) - then get all child nodes, and put those in your existing document.body.

The <div> + data + </div> is necessary because $(html) requires a single element (and children) and won't accept the standard HTML boilerplate of <!DOCTYPE ...><html>...</html>. It also appears to strip out the <head> and <body> tags, leaving behind only their children. In tests I've just made, given

var data = '<!doctype html><html><head></head><body><span>Test</span>' +
           '<span>Test 2</span></body></html>'

then

$('<div>' + data + '</div>').children()

gives just the two <span> elements, i.e. the desired contents of data's <body> tag.

EDIT OK - it seems that doesn't quite work out as desired because of the stripping of the <head> and <body> tags, leaving the original contents of the <head> in place.

Try this, instead - it's a combination of your original technique, and this one:

var $newContent = $(data.substring(data.indexOf('<body')));
$(document.body).empty().append($newContent);
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • This is a much more sensible solution than mine, +1. It's less fun, though. – lonesomeday Oct 31 '13 at 20:51
  • 1
    @lonesomeday the problem with your answer is that it addresses the problem the OP _thought_ he had, not the one he actually has ;-) – Alnitak Oct 31 '13 at 20:52
  • That's an interesting reflection on what makes a good programmer: it's not knowing the answers, but knowing the right questions... – lonesomeday Oct 31 '13 at 20:54
  • Almost there, the returned data in my ajax request contains a whole html document, including doctype, html tag and the rest. $(data).children() doesn't contain all elements in the body tag. $(data).find('body') didn't work either. $(data).children() works if I put 2 nested in my (it gets the content in the 2nd div, but that's not the behaviour I want to see – Milanzor Oct 31 '13 at 21:03
  • @Milfert right - that's because `$(data)` only works if `data` is a single node, but you've got two nodes - `doctype` and `html`. – Alnitak Oct 31 '13 at 21:06
  • @Alnitak yeah, is there a way to fix this? – Milanzor Oct 31 '13 at 21:08
  • @Milfert it looks like my code above will work if you use `$('
    ' + data + '
    ').children()`.
    – Alnitak Oct 31 '13 at 21:08
  • @Alnitak really appreciating your help. When jQuery parses a string to a jQuery object, it removes the body tag. Check my fiddle: http://jsfiddle.net/neJHN/1 ---- Didn't see your edit. Yeah, it should do that. Check updated fiddle – Milanzor Oct 31 '13 at 21:17
  • @Milfert yes - I found that too - my answer was already updated to show that. – Alnitak Oct 31 '13 at 21:19
  • @Milfert also, don't forget that the point of my answer is _not_ to find the body tag string itself, but to find the _contents_ of it, since that's what you're going to put into your page. – Alnitak Oct 31 '13 at 21:21
  • @Alnitak Yeah, very true, noted. Thanks – Milanzor Oct 31 '13 at 21:23
  • If I console.log $(data), the tag isnt even in the list. Doesn't that mean I will never be able to retrieve its contents, as it's not in the jQuery object? http://jsfiddle.net/neJHN/2 – Milanzor Oct 31 '13 at 21:30
  • @Milfert I'm looking into that - trying to find definitive documentation. – Alnitak Oct 31 '13 at 21:32
  • @Alnitak - Did you find anything? I found this explanation: http://stackoverflow.com/a/12808939/2003600 -> "Problem is that jQuery creates a DIV and sets innerHTML and then takes DIV children, but since BODY and HEAD elements are not valid DIV childs, then those are not created by browser." – Milanzor Nov 01 '13 at 10:35
  • @Milfert there's stuff in the jQuery docs (http://api.jquery.com/jQuery/#jQuery2) that says that the browser itself _may_ remove the `` (and other tags). That said, do you still have any need to get the actual tag? I thought the ultimate aim was to just get the body's contents. – Alnitak Nov 01 '13 at 10:38
  • @Alnitak I don't need the tag itself, I need all it's contents. But if that's not possible, I will need the tag to get the contents using substring. I'm one step further though, is not a valid child of
    , so it doesn't get parsed into it. If I use a non-existing tag, it does parse the contents of and into the jQuery object. Check this updated fiddle: http://jsfiddle.net/neJHN/3/ -> All that remains is removing the tags that are supposed to be in the
    – Milanzor Nov 01 '13 at 10:42
  • Ah - I see - the contents of the `` are appearing too... damn – Alnitak Nov 01 '13 at 10:49
  • @Alnitak your indexOf(' – Milanzor Nov 01 '13 at 13:27
  • @Alnitak as I'm pretty new to posting on SO, is it appropriate to answer my own question, putting a detailed description in and mark that as the answer? I'd love to give you the creds and you helped me ALOT, but it's not the actual answer. Thanks alot still, you showed me that helping out on SO is alot of fun, so will be doing that alot more myself. – Milanzor Nov 01 '13 at 14:40
  • @Milfert sure, go ahead. – Alnitak Nov 01 '13 at 14:49
1
<body id="mybody" class="someclass">
    blah blah...
</body>

To get what you want use this:

As @lonesomeday mentioned not all browsers support outerHTML so you have to use your own implementation of it:

jQuery.fn.outerHTML = function() {
    return $("<p>").append(this.eq(0).clone()).html();
};

And then use it to receive what you want:

var html = $(document.body).outerHTML(),
    html = html.substr(0, html.indexOf('>')+1);

So html variable contains <body id="mybody" class="someclass">

Damian Drygiel
  • 17,900
  • 4
  • 35
  • 28
0

it's better to avoid parsing yourself. you might want to look at https://unifiedjs.com/ - a lot of plugins to do parsing related work.

Lior
  • 40,466
  • 12
  • 38
  • 40