38

I'm writing tests with QUnit and using $.ajax() to pull in HTML for some of the tests from the the dev site running on my local:

add_elements = function(location, selector) { 
  $.ajax(location, {async: false}).done(function(data) {
    stor.$els = $(selector, $.parseHTML(data));
    stor.$els.appendTo($('body'));
  })
}

Using this function at a certain location, I get the following data passed to my .done() callback:

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Home</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="/static/css/bootstrap.min.css" rel="stylesheet" media="screen">

</head>

<body>
<div id="app-container" class="container-fluid">
    <div class="page-header">
        <h1>
            <a href="/">Home</a>
            <small>Text</small>
        </h1>
    </div>

    <div id="hero-units" class="carousel-inner slide">

        <div class="hero-unit home item active">
            <h1>
                Text text text
            </h1>
            <p>
                More text!
            </p>
            <div id="app-nav">
                <a id="lets-go" class="btn btn-primary btn-large nav-forward" href="/what-up/">
                    Let's go
                </a>
            </div>
        </div>

    </div>
</div>

<script src="/static/js/jquery.js"></script>
<script src="/static/js/underscore.js"></script>
<script src="/static/js/backbone.js"></script>
<script src="/static/js/bootstrap.js"></script>
<script src="/static/js/site-fiddle.js"></script>
<script src="/static/js/site.js"></script>

</body>
</html>

Everything works if selector is #hero-units or .hero-unit, but $(selector, $.parseHTML(data)) returns nothing if selector is #app-container! And I want a jQuery object for the div#app-container element.

And here is what kills me:

  • $.parseHTML(data) does contain the div#app-container element. It's just $.parseHTML(data)[7].
  • Yet, $('#app-container', $.parseHTML(data)) is an empty array.
  • $('div', $.parseHTML(data)) includes all the divs inside of div#app-container, but not div#app-container itself.

What's going on here? It appears that what's happening is that $ is not looking at any of the top-level elements returned by $.parseHTML(data) or $($.parseHTML(data))), and just their children.

How can I get a jQuery object for div#app-container from this $.parseHTML(data)?

ANSWER

The $(selector, $.parseHTML(data))-style lookup uses $.find. Since I'm looking for an element that's top-level in this jQuery object, I should use $.filter instead. Voila.

2
  • 1
    thanks for your update with filter much nicer than the answer below. although that would also have worked :) Commented Jun 2, 2014 at 15:00
  • 1
    Can you provide an example of $.filter working to get the outermost node of the output from $.parseHTML? How do you pass the output of parseHTML to $.filter ? It only accepts one argument. Commented Nov 20, 2014 at 17:33

7 Answers 7

42

You need to create a DOM element to append the results of .parseHTML() first so that it creates a DOM tree before jQuery can traverse it and find div#app-container.

var tempDom = $('<output>').append($.parseHTML(str));
var appContainer = $('#app-container', tempDom);

I used your example and got it working: http://jsfiddle.net/gEYgZ/

The .parseHTML() function seems to choke on the <script> tags, so I had to remove them.

PS. Obviously <output> is not a real HTML tag, just using it for the example

Sign up to request clarification or add additional context in comments.

9 Comments

Yeah, you're right. It needs to be appended inside of something first, and then searched. jQuery isn't finding top-level tags in this case. I wonder why.
Thanks for the fiddle. I was thinking of making one. Should have done that before posting.
@dimadima It's not about top-level tags, the result of $.parseHTML(str) is not a DOM element yet. It's kind of in a purgatory state, if you will, and needs to be brought into the DOM world. Either by .append(), .html(), etc.
really? i would have thought wrapping it in $() would be sufficient. like, from jquery.com/upgrade-guide/1.9: If a string is known to be HTML but may start with arbitrary text that is not an HTML tag, pass it to jQuery.parseHTML() which will return an array of DOM nodes representing the markup. A jQuery collection can be created from this, for example: $($.parseHTML(htmlString)). This would be considered best practice when processing HTML templates for example."
@dimadima Maybe bring this up with the folks at jQuery :P
|
14

You can try this:

$($.parseHTML('<div><span id="foo">hello</span></div>')).find('#foo');

for strings that start with < you can shorter that code to just:

$('<div><span id="foo">hello</span></div>').find('#foo');

4 Comments

Ah, right, but what about $($.parseHTML('<div><span id="foo">hello</span></div>')).find('div')? Yields an empty array. Anyway, the .find() approach above is the same as $('#foo', $($.parseHTML('<div><span id="foo">hello</span></div>')))
@dimadima it return element for me, I tested before I post the answer.
@dimadima it wont find div because it the root of the html if you want to find div then just use $($.parseHTML('<div><span id="foo">hello</span></div>')) and you got div.
Works. Notice the double $
4

Just ran into this.

Turns out $.parseHTML returns a vanilla Array of DOM elements, and that doesn't support the jQuery find that you want.

I think this is the cleanest solution for converting an HTML string to a jQuery object:

var html = '<div><span id="foo">hello</span></div>';
var $foo = $(html).find('#foo');

Note: You may want to use $.trim around your html strings so jQuery doesn't get confused by whitespaces.

Comments

4

It doesn't have anything to do with the html not being part of the DOM. It's just that for whatever reason the top level tag(s) in the parsed HTML cannot be found. If the element you are looking for is wrapped in another tag, then the find works as you expect. BTW, wrapping the element in the body tag won't work, but all the others I have tried work fine.

var bad = $.parseHTML('<ul><<li><one/li>/<li>two</li></ul>');
console.log($(bad).find('li').length);  //will be 2
console.log($(bad).find('ul').length);  //will be 0 

var good = $.parseHTML('<div><ul><<li><one/li>/<li>two</li></ul></div>');
console.log($(good).find('li').length); //will be 2
console.log($(good).find('ul').length); //will be 1

This is definitely the case in jQuery 1.11, not sure what happens in later versions. Here is a fiddle that demonstrates: http://jsfiddle.net/ndnnhf94/

Comments

2

Above answers didn't work for me, this did:

use jQuery.parseHTML to parse the HTML into an array of elements; then you’ll be able to convert it to a jQuery collection and use filter to restrict the collection to elements matching a selector.

var html =
'<td class="test">asd</td>' +
'<td class="last">jkl</td>';
var obj = $($.parseHTML(html)).filter('.test');

Comments

0

i think you should try with this:

$('body', $.parseHTML(data))

5 Comments

That's not related to my problem. My problem is that div#app-container is not being found by jQuery. I appreciate your time, but if you have more time, please re-read the question.
Umm...it just a suggestion try with this.
That is also an empty array.
What's happening is that $ is not looking at any of the top-level elements returned by $.parseHTML(data) or $($.parseHTML(data))), and just their children.
why not create a elem with that id and append in that one.
0

I believe this will work, especially if you specifically need to find the element by id;

function findInParsed(html, selector){
    return $(selector, html).get(0) || $(html).filter(selector).get(0);
}

If you need the jquery version of the object then you can get it with this

function findInParsed(html, selector){
    var check = $(selector, html).get(0);
    if(check)
        return $(check);
    check = $(html).filter(selector).get(0)
    return (check)? $(check) : false;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.