6

I'm loading some HTML with jQuery asynchronously:

$.get(path, {}, function (data) {
    var result = $(data);
    var resultSelector = result.find(selector);
});

result is a valid HTML that contains my selector (in my specific case, "#UsersAndRolesResults").

I can see that it contains when I simply type result into the console, it's there exactly with the same ID, no typos or anything.

However, result.find(selector) returns 0 elements.

In my specific example, this is result:

enter image description here

And:

enter image description here

Why?

UPDATE: I can query for other elements that are inside #UsersAndRolesResults with their ID tag and they are returned correctly. I also cannot query any other top-level elements in the result. I think there is an issue with querying top-level elements inside the result.

UPDATE 2: Just try $('<div id="target"></div>').find("#target") and you will get 0 results, where you should obviously get the div.

Can Poyrazoğlu
  • 33,241
  • 48
  • 191
  • 389
  • If I'm reading this correctly, I think it's because the "result" is not yet part of the DOM. It hasn't been bound to anything. – WonderGrub Jun 26 '16 at 03:28
  • @WonderGrub yes it's not added to DOM, and I *can't* add it to DOM as I need to grab only the element with that ID, and replace an element that already exists on the DOM with the same ID. I don't think it's a good idea to have two elements with the same ID simultaneously in the DOM. – Can Poyrazoğlu Jun 26 '16 at 03:32
  • Is `result` an entire `document`? – guest271314 Jun 26 '16 at 03:53
  • @guest271314 no, it's just part of a document – Can Poyrazoğlu Jun 26 '16 at 03:54
  • Can you reproduce at plnkr http://plnkr.co or jsfiddle http://jsfiddle.net? – guest271314 Jun 26 '16 at 03:55
  • @guest271314 I've already solved the issue, see my answer – Can Poyrazoğlu Jun 26 '16 at 03:56
  • @CanPoyrazoğlu _"Just try `$('
    ').find("#target")` and you will get 0 results, where you should obviously get the div"_ ? `#target` would not be an expected result of `.find()` chained to `$('
    ')`. `.find()` searches for child elements of selector
    – guest271314 Jun 26 '16 at 04:03
  • `.find()` searches for child elements. Use `.filter()` for top level elements. – Shaunak D Jun 26 '16 at 04:06

4 Answers4

6

No. This is not bug, this is behavior defined in jQuery.

find() is used to select the elements which are inside the element on which find is called. In your case, the element is not children of the selector, so find will not work.

Example:

<div id="container">
    <div class="message">Hello World!</div>
    <div>Bye World</div>
</div>

JavaScript:

$('#container').find('.message');

This will select the element having class message and which is inside the element having ID container.

But, if you use find, it'll return nothing i.e. empty array since there is no element #container inside #container.

$('#container').find('#container');

Your code is equivalent to this ^^^.


If you want, you can use filter. filter will go through each element and check if this matches the selector, if it then the element is added to the result set.

$('#container').filter('#container');

This'll give you complete element.

Tushar
  • 85,780
  • 21
  • 159
  • 179
  • Oh, I didn't know about `filter`. Great. – Can Poyrazoğlu Jun 26 '16 at 04:07
  • 1
    `.closest()` would return same result – guest271314 Jun 26 '16 at 04:09
  • @guest271314 NO. OP have a HTML code as string and want to get `top` level element like the example I've given. – Tushar Jun 26 '16 at 04:10
  • @Tushar _"NO. OP have a HTML code as string and want to get top level element like the example I've given."_ Actually, OP has not included what `data` is at original Question. Yes, `.closest()` would traverse both current selector, that is string wrapped in call to `jQuery()`, and return jQuery object representing selector if parameter to `.closest()` is selector within string passed to `jQuery()`, see http://stackoverflow.com/a/38035135/ – guest271314 Jun 26 '16 at 04:14
  • @CanPoyrazoğlu Can you add the HTML in question and do you always want to get top level element? – Tushar Jun 26 '16 at 04:22
  • @Tushar Note, given image at Question, `data` would not be begin with `#container` or `#UsersAndRolesResults` element – guest271314 Jun 26 '16 at 04:53
  • @guest271314 The `container` was just an example and you can see the comma between elements. – Tushar Jun 26 '16 at 04:54
  • The original description of `data`, image containing `#UsersAndRolesResults` element where `.row` is first element in `data`, and later example `html`, `js` at OP _"Just try `$('
    ').find("#target")`"_ do not appear to match
    – guest271314 Jun 26 '16 at 04:55
2

It seems to be a design decision with jQuery. Top-level elements in an AJAX result are not queried correctly with find. Interesting.

I've solved my problem with a workaround by creating a dummy div element, encapsulating my result inside that element, and then querying that dummy element. It worked:

var t = $("<div>")
t.append(result);
t.find("#UsersAndRolesResults"); //this one returned my object correctly

For a simple example, try:

$('<div id="target"></div>').find("#target");

You will get 0 results.

Try:

$('<div><div id="target"></div></div>').find("#target")

And you'll get the correct result.

Can Poyrazoğlu
  • 33,241
  • 48
  • 191
  • 389
  • Interesting, in my reduced case I wrapped my 2x `
    ` elements in a parent container by default.
    – danjah Jun 26 '16 at 03:49
  • 1
    If I remove the wrapping element from the sample HTML data, I get 0 Nodes returned - a bug you have found. – danjah Jun 26 '16 at 03:55
  • _"It seems to be a bug with jQuery"_ Can you include `html` string or array `data` at Answer? For ability to reproduce initially described issue, see http://stackoverflow.com/help/mcve – guest271314 Jun 26 '16 at 03:58
  • `$('
    ').find("#target");` would not be expected to return `#target`; `.find()` searches for child elements
    – guest271314 Jun 26 '16 at 04:05
  • @guest271314 oh, then it's a design issue. anyway, I've updated my answer accordingly. – Can Poyrazoğlu Jun 26 '16 at 04:06
  • @CanPoyrazoğlu It is not an issue, you can utilize methods which traverse both current selector and child elements. Still not certain what `data` is at original Question? – guest271314 Jun 26 '16 at 04:11
  • @guest271314 I can't paste the `data` itself directly for various reasons, but as I've posted in the screenshot, it's an HTML string (not a whole document) with some elements at the root level. – Can Poyrazoğlu Jun 26 '16 at 04:15
  • @CanPoyrazoğlu _"I can't paste the data itself directly"_ You could create a representation of `data`, whether string or array, without pasting exact `data` or `html`. You could change tag names and `id`s, `className`s, while still conveying form of original `data`, without compromising any portion of original `data`. How to reproduce the issue without representation of `html` which caused issue? – guest271314 Jun 26 '16 at 04:24
  • @guest271314 I've already did exactly what you said and demoed the issue as you an see at the end of my answer. It's actually simple and very similar to that one-liner at the end, and exhibits the same behavior. – Can Poyrazoğlu Jun 26 '16 at 04:30
  • @CanPoyrazoğlu Image of `result` does not appear to be same as `
    `. _"It's actually simple and very similar to that one-liner at the end,"_ Could you reproduce `data` as a text value, with `id`'s, tag names, `className`s changes as text, instead of image?
    – guest271314 Jun 26 '16 at 04:32
  • @guest271314 I didn't tell its the same, I've told that it's very similar. And by similar I'm not talking about the names etc, I'm talking about the structure. Does it really matter? I don't understand why you are insisting on me posting `data`? Isn't **an example that reproduces the exact same issue** enough? – Can Poyrazoğlu Jun 26 '16 at 04:56
  • The image at OP and example `html` later provided do not match; is not exact same as depicted at image. Why should viewers of Question ignore this? – guest271314 Jun 26 '16 at 05:03
  • @guest271314 No one said that they exactly match. in the answer, I've just posted an **example** and marked it as **a simple example** clearly, which would be more than enough for anyone who can simply think analytically, knows some javascript, reads the question, and understands the context. – Can Poyrazoğlu Jun 26 '16 at 05:16
1

Try this:

$.get(path, {}, function (data) {
    var result = $($.parseHTML(data));
    var resultSelector = result.find(selector);
});
Brian Sternari
  • 501
  • 4
  • 10
0

Given

console.log($("<div id=target></div>").find("#target"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

.length would be 0 as expected.


You can use .closest() to search for both child elements and original jQuery object selector

console.log($("<div id=target></div>").closest("#target"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
guest271314
  • 1
  • 15
  • 104
  • 177
  • wouldn't `filter` be a better option (I've just learned it from another answer)? as travesing up a tree with `closest` would feel a bit awkward; I'm already querying the top-level element and it wouldn't make sense to traverse up, but it does make sense to travese down. – Can Poyrazoğlu Jun 26 '16 at 04:13
  • @CanPoyrazoğlu `.closest()` traverses current element; given `.find()` does not return expected result of query for an `id`, if element is in fact within `document`, could only be current selector or previous element within `document` – guest271314 Jun 26 '16 at 04:17
  • @Tushar _"Tried to run second snippet in console, got this VM823:211 Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '
    ' is not a valid selector."_ Does not return that result, here. Have noticed stacksnippets freezing following addition of `console`. Will create a plnkr
    – guest271314 Jun 26 '16 at 04:17
  • Yes, that works :). Only problem I see is that it'll return only one element when used with general selector e.g. class selector. As you can see in question there are two elements having `row` class. So, `$(thatHTMLString).closest('.row')` will return .... Whereas `filter` will return all the elements having `row` class. CC @CanPoyrazoğlu – Tushar Jun 26 '16 at 04:32
  • @Tushar OP appeared to be traversing for single element having an `id`, not multiple elements. though, again, still not certain what `data` is at OP? Image depicted does not appear to match description _"result is a valid HTML that contains my selector (in my specific case, "#UsersAndRolesResults")."_ or example `"
    "`, where image at OP displays elements before `#UsersAndRolesResults`? `#UsersAndRolesResults` : data` would not be `
    `
    – guest271314 Jun 26 '16 at 04:39