Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jquery.html() returns only first text when loaded from string

Supposedly a jquery object can be initialized from string. This can often happen when processing ajax results, i.e. I'm trying to replicate http://api.jquery.com/jQuery.post/

However, I'm seeing strange behavior:

function test() {
    var content = $("<html><body><div>hello</div><div>world</div></body></html>");
    alert("content.text() = " + content.text());
    alert("content.html() = " + content.html());
}

The first alert shows: content.text() = helloworld

The second alert shows: content.html() = hello

What's happening here?

Solution

Thanks everyone for the explanations. I ended up adding another layer of <div> to have a single child of <body>, as in

<html>
 <body>
  <div>    <=== added
   <div>hello</div>
   <div>world</div>
  </div>
 </body>
</html>
like image 925
Rudiger W. Avatar asked May 03 '15 09:05

Rudiger W.


2 Answers

When parsing HTML fragments containing a body element, browsers in general (and jQuery does this as well) will disregard everything except what's inside the body element. So what you have there ends up being equivalent to:

var content = $("<div>hello</div><div>world</div>");
alert("content.text() = " + content.text());
alert("content.html() = " + content.html());

You end up with a jQuery object with two elements in it: The div elements.

In jQuery, normally accessor functions (html, val, css, etc.) only use the first element in the set when you use them as getters, and that's what html is doing above. text is an unusual accessor function in jQuery: It gives you the combined text of all of the elements in the set, not just the first.

We can see this in the docs, but it's still surprising. From html:

Get the HTML contents of the first element in the set of matched elements or set the HTML contents of every matched element.

From text:

Get the combined text contents of each element in the set of matched elements, including their descendants, or set the text contents of the matched elements.

(My emphasis in both cases.)

like image 152
T.J. Crowder Avatar answered Nov 10 '22 14:11

T.J. Crowder


Browsers remove those html and body elements. The content collection has only 2 div elements.

When passing in complex HTML, some browsers may not generate a DOM that exactly replicates the HTML source provided. As mentioned, jQuery uses the browser's .innerHTML property to parse the passed HTML and insert it into the current document. During this process, some browsers filter out certain elements such as <html>, <title>, or <head> elements. As a result, the elements inserted may not be representative of the original string passed.

This is the string representation of your content collection:

content.map(function() { return this.outerHTML || this.nodeValue; }).get().join('');
// -> <div>hello</div><div>world</div>

.text() method returns textContent/nodeValue of all the elements in the collection:

content.text(); // -> helloworld

.and .html() method returns innerHTML of the first element in the collection:

content.html(); // -> hello
like image 28
undefined Avatar answered Nov 10 '22 14:11

undefined