Nested jQuery.each() - continue/break
Asked Answered
G

11

45

Consider the following code:

    var sentences = [
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
        'Vivamus aliquet nisl quis velit ornare tempor.',
        'Cras sit amet neque ante, eu ultrices est.',
        'Integer id lectus id nunc venenatis gravida nec eget dolor.',
        'Suspendisse imperdiet turpis ut justo ultricies a aliquet tortor ultrices.'
    ];

    var words = ['ipsum', 'amet', 'elit'];

    $(sentences).each(function() {
        var s = this;
        alert(s);
        $(words).each(function(i) {
            if (s.indexOf(this) > -1)
            {
                alert('found ' + this);
                return false;
            }
        });
    });

The interesting part is the nested jQuery.each() loops. As per the documentation, returning false will break out of the loop (discontinuing execution of the loop - similar to a normal JavaScript break statement), and returning non-false will stop the current iteration and continue with the next iteration (similar to a normal JavaScript continue statement).

I can break or continue a jQuery.each() on its own, but with nested jQuery.each, I've found it difficult to break out of the parent loop from within the child loop. I could use a boolean value, and update it on every child iteration, but I was wondering if there was an easier way.

I've set up an example at jsFiddle if you'd like to mess around with it. Simply click the "Test" button to run the example shown above.

TLDR: Is there anything resembling a labeled continue or break within the context of jQuery?

Grooms answered 16/7, 2010 at 17:45 Comment(2)
It looks like you're over-using jQuery here, a simple for loop will do what you want :)Issy
This is a much simplified example. In reality, I'm looping over jQuery-selected DOM nodes, etc.Grooms
I
24

You should do this without jQuery, it may not be as "pretty" but there's less going on and it's easier to do exactly what you want, like this:

var sentences = [
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
    'Vivamus aliquet nisl quis velit ornare tempor.',
    'Cras sit amet neque ante, eu ultrices est.',
    'Integer id lectus id nunc venenatis gravida nec eget dolor.',
    'Suspendisse imperdiet turpis ut justo ultricies a aliquet tortor ultrices.'
];

var words = ['ipsum', 'amet', 'elit'];

for(var s=0; s<sentences.length; s++) {
    alert(sentences[s]);
    for(var w=0; w<words.length; w++) {
        if(sentences[s].indexOf(words[w]) > -1) {
            alert('found ' + words[w]);
            return;
        }
    }
}

You can try it out here. I'm not sure if this is the exact behavior you're after, but now you're not in a closure inside a closure created by the double .each() and you can return or break whenever you want in this case.

Issy answered 16/7, 2010 at 17:50 Comment(4)
Very good point. Even with the more complicated use of jQuery DOM selectors that makes up my actual use-case, they can be iterated over normally.Grooms
Normal JavaScript gets the check. Man, I love this language.Grooms
this solution is impeccably correct, but it does not address the stated question. It does not explain how to break out of a chain of nested 'each' loops. thus -1 (sorry)Eanes
@Eanes Yes it does... Nick says you can return (break from both loops) or break (break from inner loop) using this solution.Toddtoddie
N
94

There are a lot of answers here. And it's old, but this is for anyone coming here via google. In jQuery each function

return false; is like break.

just

return; is like continue

These will emulate the behavior of break and continue.

Naevus answered 11/10, 2012 at 9:17 Comment(3)
Please see Serj's answer. It's downvoted because you can't have a labeled return true or return falseGrooms
actually I tried return true in firefox and it was the same as break for me. Which is the reason for my answer.Naevus
That doesn't break out of a nested loop.Apodaca
I
24

You should do this without jQuery, it may not be as "pretty" but there's less going on and it's easier to do exactly what you want, like this:

var sentences = [
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
    'Vivamus aliquet nisl quis velit ornare tempor.',
    'Cras sit amet neque ante, eu ultrices est.',
    'Integer id lectus id nunc venenatis gravida nec eget dolor.',
    'Suspendisse imperdiet turpis ut justo ultricies a aliquet tortor ultrices.'
];

var words = ['ipsum', 'amet', 'elit'];

for(var s=0; s<sentences.length; s++) {
    alert(sentences[s]);
    for(var w=0; w<words.length; w++) {
        if(sentences[s].indexOf(words[w]) > -1) {
            alert('found ' + words[w]);
            return;
        }
    }
}

You can try it out here. I'm not sure if this is the exact behavior you're after, but now you're not in a closure inside a closure created by the double .each() and you can return or break whenever you want in this case.

Issy answered 16/7, 2010 at 17:50 Comment(4)
Very good point. Even with the more complicated use of jQuery DOM selectors that makes up my actual use-case, they can be iterated over normally.Grooms
Normal JavaScript gets the check. Man, I love this language.Grooms
this solution is impeccably correct, but it does not address the stated question. It does not explain how to break out of a chain of nested 'each' loops. thus -1 (sorry)Eanes
@Eanes Yes it does... Nick says you can return (break from both loops) or break (break from inner loop) using this solution.Toddtoddie
W
15

As is stated in the jQuery documentation http://api.jquery.com/jQuery.each/

return true in jQuery.each is the same as a continue

return false is the same as a break

Whirlwind answered 16/8, 2012 at 15:31 Comment(1)
$(selector).each() VS $.each(set) - I hope it works the same :)Fayina
V
8

There is no clean way to do this and like @Nick mentioned above it might just be easier to use the old school way of loops as then you can control this. But if you want to stick with what you got there is one way you could handle this. I'm sure I will get some heat for this one. But...

One way you could do what you want without an if statement is to raise an error and wrap your loop with a try/catch block:

try{
$(sentences).each(function() {
    var s = this;
    alert(s);
    $(words).each(function(i) {
        if (s.indexOf(this) > -1)
        {
            alert('found ' + this);
            throw "Exit Error";
        }
    });
});
}
catch (e)
{
    alert(e)
}

Ok, let the thrashing begin.

Villainy answered 16/7, 2010 at 17:55 Comment(5)
Wow. This may be a blatant misuse of try/catch, but it works.Grooms
@Ryan yeah I know. It completely goes against what/how we are taught to do things. But I was just trying to propose a solution.Villainy
Glad to help then. Surprised not really any thrashing. Must be light today or my self acknowledgment of the not best practice approach.Villainy
Almost gave you the check on this one, but it felt too dirty :-D Seriously, though, clever solution.Grooms
I like it too! This is exactly why we call it "throwing an exception" now instead of "raising an error". One need only change "Exit Error" to a more diplomatic "Exit condition encountered" or some such, and we have laundered the concept and made it politically correct. :) One might say (if one is given to spin doctoring) that the rule here is that one loops "except" when an exit condition is encountered, at which point one throws an exception.Tugboat
A
8

The problem here is that while you can return false from within the .each callback, the .each function itself returns the jQuery object. So you have to return a false at both levels to stop the iteration of the loop. Also since there is not way to know if the inner .each found a match or not, we will have to use a shared variable using a closure that gets updated.

Each inner iteration of words refers to the same notFound variable, so we just need to update it when a match is found, and then return it. The outer closure already has a reference to it, so it can break out when needed.

$(sentences).each(function() {
    var s = this;
    var notFound = true;

    $(words).each(function() {
        return (notFound = (s.indexOf(this) == -1));
    });

    return notFound;
});

You can try your example here.

Anus answered 16/7, 2010 at 17:59 Comment(0)
N
8

I've used a "breakout" pattern for this:

$(sentences).each(function() {
    var breakout;
    var s = this;
    alert(s);
    $(words).each(function(i) {
        if (s.indexOf(this) > -1)
        {
            alert('found ' + this);
            return breakout = false;
        }
    });
    return breakout;
});

This works nicely to any nesting depth. breakout is a simple flag. It will stay undefined unless and until you set it to false (as I do in my return statement as illustrated above). All you have to do is:

  1. declare it in your outermost closure: var breakout;
  2. add it to your return false statement(s): return breakout = false
  3. return breakout in your outer closure(s).

Not too inelegant, right? ...works for me anyway.

Neurology answered 2/4, 2014 at 3:59 Comment(1)
this is really nice, it is a shame that all the other answers - which aside from the selected answer are just wrong have been upvoted.Hydrosol
P
3

Unfortunately no. The problem here is that the iteration happens inside functions, so they aren't like normal loops. The only way you can "break" out of a function is by returning or by throwing an exception. So yes, using a boolean flag seems to be the only reasonable way to "break" out of the outer "loop".

Purely answered 16/7, 2010 at 17:50 Comment(0)
U
3

return true not work

return false working

found = false;
query = "foo";

$('.items').each(function()
{
  if($(this).text() == query)
  {
    found = true;
    return false;
  }
});
Uncharted answered 18/3, 2011 at 0:23 Comment(0)
R
2

Labeled Break

outerloop:
$(sentences).each(function() 
{
    $(words).each(function(i) 
    {
        break; /* breaks inner loop */
    } 
    $(words).each(function(i)  
    {
        break outerloop; /* breaks outer loop */
    }
}
Refulgent answered 15/5, 2012 at 14:29 Comment(1)
Does it really work? Labeled breaks over function boundaries?Jospeh
S
2

Confirm in API documentation http://api.jquery.com/jQuery.each/ say:

We can break the $.each() loop at a particular iteration by making the callback function return false. Returning non-false is the same as a continue statement in a for loop; it will skip immediately to the next iteration.

and this is my example http://jsfiddle.net/r6jqP/

(function($){
    $('#go').on('click',function(){
        var i=0,
            all=0;
        $('li').each(function(){
             all++;
             if($('#mytext').val()=='continue')return true;
             i++;
             if($('#mytext').val()==$(this).html()){
                 return false;
             }
        });
        alert('Iterazione : '+i+' to '+all);
    });
}(jQuery));
Squaw answered 15/6, 2013 at 12:53 Comment(0)
A
0

You can break the $.each() loop at a particular iteration by making the callback function return false.

Returning non-false is the same as a continue statement in a for loop; it will skip immediately to the next iteration.

Aeronaut answered 11/3, 2020 at 15:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.