问题
When used with the child selector >
, the two variants of jQuery's "has" behave differently.
Take this HTML:
<div>
<span>Text</span>
</div>
Now:
$("div:has(>span)");
would return it, while:
$("div").has(">span");
would not. Is it a bug or a feature? Compare here: http://jsfiddle.net/aC9dP/
EDIT: This may be a bug or at least undocumented inconsistent behavior.
Anyway, I think it would be beneficial to have the child selector consistently work as an unary operator. It enables you to do something that otherwise would require a custom filter function — it lets you directly select elements that have certain children:
$("ul:has(>li.active)").show(); // works
$("ul").has(">li.active)").show(); // doesn't work, but IMHO it should
as opposed to:
$("ul").filter(function () {
return $(this).children("li.active").length > 0;
}).show();
I've opened a jQuery ticket (7205) for this.
回答1:
This happens because the sizzle selector is looking at all Div's that have span children in the :has example. But in the .has example, it's passing all DIV's to the .has(), which then looks for something that shouldn't be a stand-alone selection. ("Has children of nothing").
Basically, :has() is part of the selection, but .has() gets passed those divs and then re-selects from them.
Ideally, you don't use selectors like this. The > being in the selector is probably a bug, as it's semantically awkward. Note: the child operator isn't meant to be stand-alone.
Sizzle vs target.sizzle:
I'm always talking about v1.4.2 of jquery development release.
.has (line 3748 of jQuery)
Description: Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element.
Code:
var targets = jQuery( target );
return this.filter(function() {
for ( var i = 0, l = targets.length; i < l; i++ ) {
if ( jQuery.contains( this, targets[i] ) ) { //Calls line 3642
return true;
}
}
});
Line 3642 relates to a 2008 plugin compareDocumentPosition, but the important bit here is that we're now basically just running two jquery queries here, where the first one selects $("DIV")
and the next one selects $(">span")
(which returns null), then we check for children.
:has (line 3129 of jQuery)
Description: Selects elements which contain at least one element that matches the specified selector.
Code:
return !!Sizzle( match[3], elem ).length;
They are two differnt tools, the :has uses sizzle 100%, and .has uses targets passed to it.
Note: if you think this is a bug, go fill out the bug ticket.
回答2:
I think you may have hit upon a genuine bug. The problem may lie in the way you are using the child selector. As user257493 pointed out, it's not meant to be used on its own (or at least I don't see any examples of that in the documentation.
Check this out though. If you add a *
before the child selector in the .has()
, suddenly it works: http://jsfiddle.net/Ender/FjgZn/
But if you do the same thing in the :has()
selector, it stops working! See here:
http://jsfiddle.net/Ender/FjgZn/
There definitely seems to be difference in the way these two are implemented.
来源:https://stackoverflow.com/questions/3944878/jquery-subtle-difference-between-has-and-has