How can I refresh a stored and snapshotted jquery selector variable

Viewed 26602

I ran yesterday in a problem with a jquery-selector I assigned to a variable and it's driving me mad.

Here is a jsfiddle with testcase:

  • assign the .elem to my obj var
  • log both lengths to the console. Result => 4
  • Remove #3 from the DOM
  • log obj to the console => the removed #3 is still there and the length is still 4. I figured out that jquery query is snapshotted? to the variable and can't?won't? be updated
  • log .elem to the console.. yep Result => 3 and the #3 is gone
  • Now I update .elem with a new width of 300
  • logging obj & obj.width gives me 300.. So the snapshot has been updated ? What's interesting is that 3 of the 4 divs have the new width, but the removed #3 doesn't...

Another test: Adding a li element to the domtree and logging obj and .elem. .elem does have the new li and obj doesn't, because it's still the old snapshot

http://jsfiddle.net/CBDUK/1/

Is there no way to update this obj with the new content? I don't want to make a new obj, because in my application there is a lot information saved in that object, I don't want to destroy...

6 Answers

Clean and generic solution worked properly with jQuery 3.4.1:

My solution is to do the following:

  1. Intercept the selector at the time of jQuery object initialization and in the same time maintain all other jQuery functionalities transparently all this using inheritance
  2. Build refresh plugin that make use of the new "selector" property we added during initialization

Definition:

$ = (function (originalJQuery) 
{
    return (function () 
    {
        var newJQuery = originalJQuery.apply(this, arguments);
        newJQuery.selector = arguments.length > 0 ? arguments[0] : null;
        return newJQuery;
    });
})($);

$.fn = $.prototype = jQuery.fn;

$.fn.refresh = function () 
{
    if (this.selector != null && (typeof this.selector === 'string' || this.selector instanceof String))
    {
        var elems = $(this.selector);
        this.splice(0, this.length);
        this.push.apply(this, elems);
    }
    return this;
};

Usage:

var myAnchors = $('p > a');
//Manipulate your DOM and make changes to be captured by the refresh plugin....
myAnchors.refresh();
//Now, myAnchors variable will hold a fresh snapshot 

Note: As optimization, object selectors don't need refresh as they are pass by reference by nature so, in refresh plugin, we only refresh if the selector is a string selector not object selector for clarification, consider the following code:

// Define a plain object
var foo = { foo: "bar", hello: "world" };
 
// Pass it to the jQuery function
var $foo = $( foo );
 
// Test accessing property values
var test1 = $foo.prop( "foo" ); // bar

// Change the original object
foo.foo = "koko";

// Test updated property value
var test2 = $foo.prop( "foo" ); // koko

You can also return the JQuery selector in a function, and save this function into the variable. Your code will look a bit different but it works. Every time when you execute the function, your jquery selector will search the DOM again.

In this example I used an arrow function without brackets which will return whatever is next to arrow. In this case it will return the JQuery collection.

const $mySelector = () => $('.selector');
console.log($mySelector().last().text());
$('.parent').append('<li class="selector">4</li>')

console.log($mySelector().last().text()); //RETURNS 4 not 3
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="parent">
    <li class="selector">1</li>
    <li class="selector">2</li>
    <li class="selector">3</li>
</ul>

Related