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:
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
Jquery .selector is deprecated, it's better to remeber string with selector value to some variable at the moment when you assign
function someModule($selector, selectorText) {
var $moduleSelector = $selector;
var moduleSelectorText = selectorText;
var onSelectorRefresh = function() {
$moduleSelector = $(moduleSelectorText);
}
}
https://api.jquery.com/selector/
Clean and generic solution worked properly with jQuery 3.4.1:
My solution is to do the following:
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.
If you use remove()
it will remove only a part of the DOM but not all the children or related, instead if you use empty()
on the element the problem is gone.
E.G.:
$('#parent .child).find('#foo').empty();
Maybe it can be useful to someone!
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 = () => $('.parent').find('.child').last();
console.log(mySelector().text());
$('.parent').append('<li class="child">4</li>')
console.log(mySelector().text());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="parent">
<li class="child">1</li>
<li class="child">2</li>
<li class="child">3</li>
</ul>