noamyg
noamyg

Reputation: 3104

How to change selector result within an "each" loop

I have this code:

var elementSelector = 'select.myClass';
$(elementSelector).each(function(i){
    /*
     * if (several conditions return true) {
     */
        doSomeDOMChanges(); //Change the DOM and add several more <select class="myClass">
    /*
     * }
     */
});

Problem is, within that loop, $(elementSelector) is not reevaluated, and its' size remains the same throughout the entire loop. $(elementSelector).length for example would stay 18, even though it actually increased to 20 within the 4th iteration.

My question is, how to force JQuery to re-select $(elementSelector) on the next iteration (without "resetting" the loop / repeating previous iterations, of course).

Thank you.

Upvotes: 1

Views: 84

Answers (2)

trincot
trincot

Reputation: 350137

If it is guaranteed that the modified DOM content occurs further in the document than the element that you are currently visiting in the loop, then you could rely on DOM methods that return a live NodeList, such as:

  • getElementsByTagName
  • getElementsByClassName
  • getElementsByName
  • children

Here is a demo where the DOM is extended with one more select element, when the value of the current select element has less than 5 characters. The newly inserted select element will have options which have values that are 1 character longer.

function doSomeDOMChanges(select) {
    $(select).after($("<select>").addClass("myClass").append(
        $("<option>").text($(select).val() + "0"),
        $("<option>").text($(select).val() + "1"),
        $("<option>").text($(select).val() + "2")
    ));
}

for (var select of document.getElementsByTagName("select")) {
    if ($(select).is(".myClass")) {
        if ($(select).val().length < 5) {
            doSomeDOMChanges(select);
        }
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select class="myClass">
    <option>2
    <option selected>3
    <option>5
</select>

The downsides:

  • You cannot get such a live collection from a call to $(), using the power of a CSS selector
  • It only works when the DOM changes are further in the document. If changes are made in content that is already "behind" the currently iterated element, the loop will not work as expected, as the new DOM element is inserted in the live collection before the currently iterated element, meaning it will not be visited, and maybe even worse: the current element will move to a higher index in that collection and be visited again by the loop.

If one of these points is a problem, you could just make the jQuery selection again, and keep a Set of the elements you had already visited. The next demo makes DOM changes before the currently visited element:

function doSomeDOMChanges(select) {
    $(select).before($("<select>").addClass("myClass").append(
        $("<option>").text($(select).val() + "0"),
        $("<option>").text($(select).val() + "1"),
        $("<option>").text($(select).val() + "2")
    ));
}

let visited = new WeakSet;
let selector = "select.myClass";
for (let more = true; more; null) {
    more = false;
    $(selector).each(function() {
        if (visited.has(this)) return;
        visited.add(this);
        if ($(this).val().length < 5) {
            doSomeDOMChanges(this);
            more = true;
        }
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select class="myClass">
    <option>2
    <option selected>3
    <option>5
</select>

Upvotes: 1

Abdul Moiz
Abdul Moiz

Reputation: 492

mark used classes with an attribute or some class, and select which are not used

var elementSelector = $('select.myClass:not[data-used=1]');
$(elementSelector).each(function(i){

    $(this).data('used', 1)
    if (several condition return true) {
        doSomeDOMChanges(); //Change the DOM and add several more <select class="myClass">
     }


});

Upvotes: 1

Related Questions