How can I refresh a stored and snapshotted jquery selector variable

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

I also liked @Esailija solution, but seems that this.selector has some bugs with filter. So I modified to my needs, maybe it will be useful to someone

This was for jQuery 1.7.2 didn`t test refresh on filtered snapshots on higher versions

$.fn.refresh = function() { // refresh seletor
    var m = this.selector.match(/\.filter\([.\S+\d?(\,\s2)]*\)/); // catch filter string
    var elems = null;
    if (m != null) { // if no filter, then do the evarage workflow
        var filter = m[0].match(/\([.\S+\d?(\,\s2)]*\)/)[0].replace(/[\(\)']+/g,'');
        this.selector = this.selector.replace(m[0],''); // remove filter from selector
        elems = $(this.selector).filter(filter); // enable filter for it
    } else {
        elems = $(this.selector);
    }
    this.splice(0, this.length);
    this.push.apply( this, elems );
    return this;
};

Code is not so beautiful, but it worked for my filtered selectors.


Yeah, it's a snapshot. Furthermore, removing an element from the page DOM tree isn't magically going to vanish all references to the element.

You can refresh it like so:

var a = $(".elem");

a = $(a.selector);

Mini-plugin:

$.fn.refresh = function() {
    return $(this.selector);
};

var a = $(".elem");

a = a.refresh();

This simple solution doesn't work with complex traversals though. You are going to have to make a parser for the .selector property to refresh the snapshot for those.

The format is like:

$("body").find("div").next(".sibling").prevAll().siblings().selector
//"body div.next(.sibling).prevAll().siblings()"

In-place mini-plugin:

$.fn.refresh = function() {
    var elems = $(this.selector);
    this.splice(0, this.length);
    this.push.apply( this, elems );
    return this;
};

var a = $(".elem");
a.refresh() //No assignment necessary