jQuery: subtle difference between .has() and :has()

百般思念 提交于 2019-12-18 12:27:08

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!