How can I refresh a stored and snapshotted jquery selector variable

后端 未结 6 1579
温柔的废话
温柔的废话 2020-12-13 13:46

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 .el
相关标签:
6条回答
  • 2020-12-13 13:49

    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
    
    0 讨论(0)
  • 2020-12-13 13:49

    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/

    0 讨论(0)
  • 2020-12-13 13:54

    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
    
    0 讨论(0)
  • 2020-12-13 13:57

    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.

    0 讨论(0)
  • 2020-12-13 14:00

    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!

    0 讨论(0)
  • 2020-12-13 14:13

    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>

    0 讨论(0)
提交回复
热议问题