MonkeyZeus
MonkeyZeus

Reputation: 20747

jQuery object caching behavior

I have this html:

<div>hi</div>
<div>bye</div>

And I run this with jQuery 1.12.3 (I doubt the version matters but it's here for reference):

$(document).ready(function(){

    var $divs = $('div');

    alert($divs.length); // Expect 2, get 2

    $('div:first').remove();

    alert($divs.length); // Expect 2, get 2

    // Why does expectation not match reality?
    // Is this not triggering a DOM search?
    alert($($divs).length); // Expect 1, get 2

    $divs = $('div');

    alert($divs.length); // Expect 1, get 1

});

Note: if you've made it this far then please notice that my question is in the comments of the jQuery code.

I'm sure this is fairly simple but I would like to know in lay-mans terms and hopefully get some direct documentation links since everything on Google guides me to endless blog posts which seem to have copy+pasted one another.


Additionally, why does this work?

html

<div>
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <p>4</p>
    <p>5</p>
</div>

jQuery

$(document).ready(function(){

    var $div = $('div');

    alert($div.find('p').length); // expect 5, get 5

    $div.find('p:first').remove();

    alert($div.find('p').length); // expect 5, get 4

});

Shouldn't $div still contain 5 <p>s?

Does .find() trigger a DOM re-query on $div?

Upvotes: 0

Views: 65

Answers (2)

Pranav C Balan
Pranav C Balan

Reputation: 115282

The $('div:first').remove(); will remove the element from the dom, but $('div') is not a live collection so which will not get updated. Also you are re-wrapping the existing jQuery object which doesn't have any effect at all, you can check it on the jQuery library code.

Lear more about jQuery object here : The jQuery Object


UPDATE :

As per the docs :

Given a jQuery object that represents a set of DOM elements, the .find() method allows us to search through the descendants of these elements in the DOM tree and construct a new jQuery object from the matching elements

find() will iterate over the elements and then by using the descendants it will generate the new jQuery object.


The find function is working as follows ( following code taken from jQuery library )

function( selector ) {
    var i,
        len = this.length,
        ret = [],
        self = this;

    if ( typeof selector !== "string" ) {
        return this.pushStack( jQuery( selector ).filter( function() {
            for ( i = 0; i < len; i++ ) {
                if ( jQuery.contains( self[ i ], this ) ) {
                    return true;
                }
            }
        } ) );
    }

    for ( i = 0; i < len; i++ ) {
        jQuery.find( selector, self[ i ], ret );
    }

    // Needed because $( selector, context ) becomes $( context ).find( selector )
    ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
    ret.selector = this.selector ? this.selector + " " + selector : selector;
    return ret;
},

Which initially selects elements from dom using selector string provided inside find method after it's filtering based on parent, so the removed element will not get selected. So find() only result the elements which is present in the dom.


FYI : In case any kind of doubts about jQuery first go through their docs, even you are not satisfied go through their library code and check the working of it.

Upvotes: 2

Starscream1984
Starscream1984

Reputation: 3062

On the line

alert($($divs).length); // Expect 1, get 2

You are still referring to the original variable which has a length of two. You are re-wrapping it as a jquery object, but that won't change it.

To reflect the DOM changes, you have to select again. Which you do before the last attempt, and there the count is correct.

This is not 'caching' behaviour, but variable behaviour.

UPDATE:

As for the second question - the find() does indeed do a DOM query as per the doc comment that Pranav C Balan included in his answer.

Upvotes: 3

Related Questions