jquery – Difference in results returned between .has() and :has() – Education Career Blog

I have a gnarly navigation structure that can be generalized as:

<ul id="navigation">
    <li>
        A
        <ul>
            <li>
                B
                <ul>
                    <li>C</li>
                </ul>
            </li>
            <li>
                D
                <ul>
                    <li>
                        E
                        <ul>
                            <li>F</li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

Sub-items are hidden until hover. I want to indicate that B, D, and E have sub-items by styling them so I used the selector:

$('#navigation > li li:has(ul)')

Which only returned B and D. Changing it to:

$('#navigation > li li').has('ul')

returned all of the correct items but I’m confused as to why.

EDIT

:has() doesn’t appear to be affected (entirely) by nesting as

$('#navigation ul > li:has(ul)')

returns the same results as .has() above.

,

From the jQuery API documentation:

:has selects elements which
contain at least one element that
matches the specified selector.

.has() reduces the set of
matched elements to those that have a
descendant that matches the selector
or DOM element.

There is a related question here: jQuery: subtle difference between .has() and :has() , that seems to point to what the difference in the two could be.

However, It appears that :has doesn’t look within a match in a nested fashion while .has() does because it returns a subset of matches as a jQuery object is able to match all descendants, even the nested one, like a regular jQuery selector.

,

The :has() selector selects only those elements that have descending elements that are matched by the given selector. Internally, :has is defined as (Sizzle is jQuery default selector library):

function(elem, i, match){
    return !!Sizzle( match3, elem ).length;
}

This is nearly equivalent to testing jQuery("selector", elem).length or jQuery(elem).find("selector").length for each selected element to filter those that don’t have such descendants.

In contrast to that, the has method can take either a selector or a DOM element and returns only those elements that do not contain any of the given elements. Because internally, the has method is defined as:

function( target ) {
    var targets = jQuery( target );
    return this.filter(function() {
        for ( var i = 0, l = targets.length; i < l; i++ ) {
            if ( jQuery.contains( this, targetsi ) ) {
                return true;
            }
        }
    });
}

So it uses the contains method to check if the given elements are contained to filter the selected elements. Note that it suffices that only one of the given elements are contained.

Leave a Comment