Breaking Out Of jQuery's Each() Iteration Method

Posted October 18, 2010 at 9:31 AM by Ben Nadel

Tags: Javascript / DHTML

In the past, I've talked about the beauty and the power of jQuery's each() method for collection iteration. But, as awesome as it is, I still find myself falling back to for-loop iteration in cases where I might need to break out of a loop, mid-collection. Today, however, at the jQuery Conference 2010, Karl Swedberg totally rocked my world in the way that only Karl knows how - he showed me that jQuery's each() method also provides this mid-iteration break functionality.

 
 
 
 
 
 
 
 
 
 

In a standard Javascript for-loop, you can always break out of a loop by using the break command within the body of the loop execution:

  • // Loop from 1 to 100.
  • for (var i = 0 ; i < 100 ; i++){
  •  
  • // Log the current iteration.
  • console.log( "Iteration", i );
  •  
  • // Break out of the for-loop.
  • break;
  •  
  • }

In this code, we are planning to loop from one to one hundred; however, we are executing the break command in the body of our iteration. As such, when the above code has finished executing, we are left with the following console output:

Iteration 0

As you can see, the loop entered its first iteration, logged one message to the console, and then completely stopped iterating. The break command that we executed not only halted the current iteration (Iteration 0), it also prevented the other 100 iterations from taking place.

As Karl demonstrated at jQCon, the jQuery each() method allows us to achieve this same kind of control flow by returning false from the iteration callback. To demonstrate this, I'm going to use each() to look for a given list item within an unordered list. Once I have found the desired list item, I am going to break out of the each() iteration and log the success:

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Breaking Out Of jQuery's Each() Method</title>
  • <script type="text/javascript" src="./jquery-1.4.3.js"></script>
  • </head>
  • <body>
  •  
  • <h1>
  • Breaking Out of jQuery's Each() Method
  • </h1>
  •  
  • <ul id="girls">
  • <li>Jill</li>
  • <li>Sarah</li>
  • <li>Kim</li>
  • <li>Tricia</li>
  • </ul>
  •  
  •  
  • <!--- When the DOM is ready (ie. now), init scripts. --->
  • <script type="text/javascript">
  •  
  • // Get the girls list collection.
  • var girls = $( "#girls" );
  •  
  • // Now, we are going to be searching for Kim; but, we don't
  • // know if we are going to find her, so set the girl to null.
  • var sarah = null;
  •  
  • // Loop over the girls, looking for Sarah.
  • girls.children().each(
  • function( index, girlNode ){
  • // Get the current girl collection.
  • var girl = $( girlNode );
  •  
  • // Log that we are checking this girl.
  • console.log( "Checking... ", girl.text() );
  •  
  • // Check to see if this girl is Sarah.
  • if (girl.text().indexOf( "Sarah" ) >= 0){
  •  
  • // Save a reference to sarah.
  • sarah = girl;
  •  
  • // Now that we have found Sarah, we don't need to
  • // keep searching. Break out of the each() loop
  • // by returning false.
  • return( false );
  •  
  • }
  • }
  • );
  •  
  • // Check to see that we found sarah.
  • if (sarah){
  •  
  • // Log that we found her.
  • console.log(
  • "That ",
  • sarah.text(),
  • "- she's so hot right now."
  • );
  •  
  • }
  •  
  • </script>
  •  
  • </body>
  • </html>

In the above code, I find all the list items in a given list. Then, I loop over them looking for the one whose text contains the phrase, "sarah." Once I find this list item, I am returning false:

  • return( false )

Doing this - returning false - within the each() iteration callback does the same thing that the break command does within a for-loop. As such, running the above code results in the following console output:

Checking... Jill
Checking... Sarah
That Sarah - she's so hot right now.

As you can see, only the first two out of the four possible list items were searched. Once the target phrase was found in the second list item, the each() iteration was halted.

There was a lot of stuff at jQuery Conference 2010 that was, without a doubt, awesome; but, sometimes it's the little things that come out of nowhere and rock your world. Being able to break out of an each()-based iteration was one of those little things. And, according to a few conversations that I had after Karl's talk, I wasn't the only one who had never seen this jQuery functionality before.




Reader Comments

Oct 18, 2010 at 12:11 PM // reply »
40 Comments

That should simplify my code somewhat. I don't know how many times I've used that same "break" method you mentioned at the top...


Oct 18, 2010 at 12:51 PM // reply »
70 Comments

And a simple "return" is the equivalent of "continue".


Oct 18, 2010 at 12:58 PM // reply »
70 Comments

After reading this article, I'm thinkin' maybe I should switch to using "return true" for "continue". Returning nothing is kinda falsy.


Oct 18, 2010 at 11:24 PM // reply »
11,246 Comments

@Brian,

Right? I'm always just surprised when I've been using something for a while and don't realize some really basic functionality exists.

@Steve,

I think the core code actually uses a triple equals for the False comparison, so returning null is not too bad.


Oct 19, 2010 at 4:21 AM // reply »
3 Comments

I don't know if I'd say my world has been rocked ;) but that's definitely a useful bit that I'm happy to know now. I wonder if other libraries do the same thing.

Also, just remember that while functional iteration is often convenient, it comes at the cost of overhead and performance. See http://www.youtube.com/watch?v=mHtdZgou0qU at 19:53 for the whole spiel, or 23:32 for the important points.


Oct 19, 2010 at 8:08 AM // reply »
11,246 Comments

@Justin,

Good point - there is always going to be a trade off of "ease of use" with "performance" any time you add an abstraction layer. The thing I like most about the each() method in general is just that every iteration gets its own variables scope so you don't run into any late-binding issues with variables.

Also, I know you left me a good comment on my composed-controller stuff - was at a conference all weekend; working on catching up on emails :)


Oct 19, 2010 at 10:05 AM // reply »
8 Comments

I didn't know you could do this in jQuery! A few days ago I learned that Underscore.js (a functional programming library for JavaScript, that compresses to about 3kb and fits nicely alongside jQuery) has a _.each() function and _.breakLoop() function. The _.breakLoop() function uses a JavaScript 1.7 feature to break the loop, when it's available.

http://documentcloud.github.com/underscore/#breakLoop


Oct 19, 2010 at 6:18 PM // reply »
11,246 Comments

@Ben,

Cool - I didn't even know that was a feature in the latest Javascript engines. As far as underscore, it was definitely praised at the jQuery Conference this past weekend.


Oct 20, 2010 at 9:37 AM // reply »
1 Comments

Hey! Sarah isn't all that. She may have the figure, but I have the boobs!


Oct 20, 2010 at 9:39 AM // reply »
11,246 Comments

@Jill,

Ha ha ha :)


Feb 21, 2011 at 4:20 PM // reply »
1 Comments

It worked !!

Thanks


Apr 25, 2012 at 5:24 AM // reply »
1 Comments

@Ben & Ben: Note that the usage of _.breakLoop() has been removed from Underscore.js since version of 1.1.3 because it isn't in the ECMAScript 5-spec.


Ano
Jul 25, 2012 at 8:04 PM // reply »
1 Comments

Within the documentation it states "We can stop the loop from within the callback function by returning false.". It also has an example of the use in it.

Documentation is great!


Oct 12, 2012 at 12:58 AM // reply »
1 Comments

Hi there Ben. I just bumped into this article because I was looking for this kind of solution, but I'm still in trouble with my code. This is the story: I am iterating through several <li> contained in several <ul> of course. Now I now how to break out of any <li> iteration but the last <ul> keeps looping endlessly. This is my code:

var x=0,y=0,dummy,dummy2,pos;
$('ul').each(function()
{
$(this).find('li').each(function(){ /* x jumps from carousel to carousel; y jumps items within a carousel */
if($("ul")[x] == undefined)
return false;
dummy = $("ul")[x].children[y].className;
if(dummy.search("focused") != -1)
{
pos = searchStringInArray(textArray,"{");
dummy2 = $("ul")[x].children[y].innerText;
textArray[pos] = dummy2;
x++;
}
else
y++;
});
});

The "return false;" statement happens when all items within all <ul> have been processed but the flow keeps going there. Have a solution? Thanks in advance.


Feb 21, 2013 at 4:03 PM // reply »
1 Comments

solved my problem thanks



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 25, 2013 at 10:08 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
@Ben, my question is that i want the current node with its tag and its parent node. i just want only that data. So, give me the solution for that. and remember solution is working on " xpath 1.0 ... read »
May 25, 2013 at 10:01 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
hey ben, i want get my current node tag and also want the root node tag withing. So, how can i fix it.. ! ... read »
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools