problem
I have an element $(elt_Main)
(<span id="a">
)
I call $(elt_Main).find(selector);
on it
With a selector selector = ':not(span span)';
But jquery is not giving out all the child elements I want.
ex [test1.html]
-
say the content in html is::
<body> <span id="a">The first <span class="red">signature <span class="green">for the .find() <span class="blue">method accepts</span> a selector</span> <strong>expression</strong> of the <span class="green">same type that we can pass to the</span> $() function</span>. The <span class="red">elements</span> will be filtered by testing whether they match this selector; all parts of the selector <strong>must lie inside of an element</strong> on which .find() is called.</span> </body>
-
the javascript is::
let elt_Main = document.getElementById('a'); // ... html_ResultAppend += hr_3; selector = ':not(span span)'; // <<< watch jqElt = $(elt_Main).find(selector); // <<< watch
-
the js that provides output for debug is::
for (let o of jqElt) { html_ResultAppend += hr_4; html_ResultAppend += o.outerHTML; }
-
the result of
jqElt
will be::<strong>expression</strong> <strong>must lie inside of an element</strong>
-
but the expecting result should be::
<span class="red">signature <span class="green">for the .find() <span class="blue">method accepts</span> a selector</span> <strong>expression</strong> of the <span class="green">same type that we can pass to the</span> $() function</span> <span class="red">elements</span> <strong>expression</strong> <strong>must lie inside of an element</strong>
-
the pb & the cause is::
these 2 elements are missing
<span class="red">signature <span class="green">for the .find() <span class="blue">method accepts</span> a selector</span> <strong>expression</strong> of the <span class="green">same type that we can pass to the</span> $() function</span> <span class="red">elements</span>
-
(I think) the logic of jquery is that (which is wrong)::
<span id="a"> <span class="red">signature ...
->
<span class="red">signature ...
is inside a span<span id="a">
-> we said
:not(span span)
-> so this should not be selected
(^ same as above for
<span id="a"> <span class="red">elements</span>
) -
my logic is that::
<span id="a">
($(elt_Main)
) is the element that invoke.find()
<span id="a">
($(elt_Main)
) itself should never be taken into account / play any effect when we use css selectors inside.find()
-
otherwise, it wont make any sense in following examples::
ex, say we do this:
$(elt_Main).find('span');
this should only find child element of
elt_Main
that is aspan
— this is correct.if we take
<span id="a">
($(elt_Main)
) into account when use use selector, this will select the<span id="a">
($(elt_Main)
) itself — this is wrong. -
in another words —
when
.find()
is invoked by<span id="a">
($(elt_Main)
),the selector should treat this (the element
<span id="a">
($(elt_Main)
) to search on) as if the tag<span id="a">
doesnt exist (– it should not play any effect), and only search on theinnerHtml
.
-
to prove my logic — example [test1.html test2.html]
-
plunker code ex: jquery
.find()
use with:not
selector is missing some elementsthe js selects the elements, and write to the html body.
-
test1 uses
.find()
+:not()
test2 uses only
:not()
test2 removed the
<span id="a">
tag in$(elt_Main)
-
(– just as I said above — we should treat it as if the tag
<span id="a">
doesnt exist) -
(if you think Im wrong on this, and you say: do not remove the
<span id="a">
makes test1 & test2 produce same result -> check out test1 vs test3 — its even worse)
-
-
we clearly see that
test1
output is missing elementstest2
output is correct
(back to the question)
"jquery .find()
use with :not
selector is missing some elements (the selector is affected by the .find()
invoking element, but it shouldnt)"
So, Is jQuery wrong? or am I wrong? Why?
Update
discussions on "different semantic of .find()
bt jQuery vs native Javascript"?
-
x
as another answer points out,
the native Javascript
querySelectorAll
does consider property from the global html file scopenot from a local sandbox scope – starting from & limited to the inside of the invoking (parent) element
AA
, which was how I understood it.-
[[ my understanding was due to
-
my inexperience with (native) Javascript
-
my personal preference on how the rule should be applied when using selector from an element
AA
is that:I prefer to select thing inside
AA
, ignoring all the effects from the outside scope — so, treat those selections inAA
as alocal sandbox scope
(again, this is just a subjective personal preference thing, but if rules are not designed in this way, of course then, I wont use it in this way)
]]
-
-> so, I was wrong on the understanding in native Javascript
-
[[ my understanding was due to
-
x
though, another problem arise:
so, the problem is not there is something wrong with
$(elt_Main).find(':not(span span)')
instead, the problem is something is wrong with
$(elt_Main).find('span span')
//doubt?—
$(elt_Main).find('span span')
selects less than what normally expected— according to the semantic in native Javascript
-> so, jQuery is wrong on this //doubt?
-
x
however, it seems the problem is actual that:
jQuery is intended to use a semantic of
find()
that is different than the semantic ofquerySelectorAll()
in native Javascript //doubt?(– see the examples below for compare)
— which mean, yes, there is a
local sandbox scope
-
x
the problem remain is then:
if "jQuery is intended to use a different semantic than its in native Javascript"
then there is actually a bug in
$(elt_Main).find(':not(span span)')
—
or if not
then
$(elt_Main).find('span span')
is bugged
example
Update
-
Added comparison with "native Javascript with
:scope
"
[[((forgot to mention))]]Turns out, it seems the native Javascript with
:scope
has the same issue then?or
:not()
was meant to view from a "global html file scope"?— which means, so, to let the effect of "local sandbox scope" play in, we need to specify
:scope :not(:scope span span)
, instead of simply:scope :not(span span)
-
[[this does output the desire output I intended to have (all the way up to) initially.
if
:not()
behaves in this way, this does clarifies something.so, with such rule (such
:not()
behaves) native Javascript has no issue;as for jQuery… may still need to fix this //doubt?]]
-
[[this does output the desire output I intended to have (all the way up to) initially.
-
jsfiddle ex: different semantic of
.find()
bt jQuery vs native Javascript (updated with:scope
)
2
Answers
Okay I have a speculation here so I’m just going to post it as an answer.
As I’ve written in my comments, essentially
$(selector).find(selector)
works like a two pass filter. It is not supposed to pose any sandbox-ish mechanism that blocks the the second pass of selection from "detecting" any fact / property of the candidates, including but not limited to their parent.HOWEVER, what I suspect about
$(A).find(A B)
is this does NOT (but IMO should) work behind the scene like this:B
and have anA
ancestor (something likeforeach c in S: if c is B and c.hasAncestor(A): nS.add(c)
)INSTEAD, it probably work behind the scene like this:
which is probably equivalent to:
As you can see, the logic is twisted in the (speculated) approach behind the scene. What you get became a "three pass filter".
Here’s an example which might give you a clearer idea on what I mean:
In other words, what
$("span").find("span span")
should (IMO) do is:But what it (seem to) really does:
As noted in the other answer, when
:not()
is involved, such problem does not occur. I suspect (again) that it is because the negation makes it harder to look right when you twist the logic as I stated.I don’t consider the current way that the descendant selector is implemented correct. I do wonder how the jQuery devs would argue about it though. (But this is really just my speculation. I haven’t check any source code or so to make sure it is 100% the case.)
Your selector asks for any element that is not a
<span>
descendant of any other<span>
. The descendant test does not apply only to the subtree beneath the starting point in the DOM (the element where the.find()
is rooted); it involves the entire DOM. It has to work that way in general. Your claim that "it shouldn’t" is a misunderstanding of how selectors work.Because your starting element is itself a
<span>
, all the<span>
elements in the subtree are descendants of a<span>
, so none of them match the selector.You can try your test with the native
.querySelectorAll()
:and see what you get.
Here is a clearer example, using a
:not()
selector that involves a parent element of the starting point:Starting from the outer span, that selector (
:not(.foo span)
) asks for any element that is not a<span>
that descends from an element with class "foo". All the<span>
tags beneath the starting point do match that selector in the:not
because they all descend from the parent<div>
. Thus the query finds only one element, the<b>
.As a general rule, finding a profound bug in a platform that’s been in use for almost 20 years is fairly unlikely. Not impossible, but unlikely.
edit — It’s pointed out in a comment that the simpler case of the selector "span span", that is, a positive search for any
<span>
that is a descendant of a<span>
results in different behavior between jQuery’s selector engine and the browser’s nativequerySelectorAll()
:I find this very surprising. It should be noted that jQuery predates the introduction of the native DOM search methods by many years, so while this could be considered wrong (I certainly do) it must be understood that there may be millions of applications that depend on the old behavior, so changing it is probably not an option for the jQuery maintainers.