Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I tell if a $("..").each() call was aborted prematurely?

First, some psuedo-psuedo-code:

$("some-selector-logic").each(function() {
  if (someLogic($(this))) {
    return false;
  }
  // Otherwise do stuff related to $(this)
});

someMoreExcitingCode();

In this example we're getting a collection of DOM elements based on some selector logic, then iterating over each one. For each element we're calling someLogic(). If that returns true we abort the each loop. Otherwise, we perform some logic on the element and then move on to the next element. Once we've worked through all the elements we continue on and call someMoreExcitingCode().

I'd like to know, before calling someMoreExcitingCode(), whether or not the loop was aborted prematurely. Obviously you can do something like this:

var aborted = false;
$("..").each(function() {
  if (someLogic($(this))) {
    aborted = true;
    return false;
  }
});

but this feels sloppy to me, like jQuery should be providing me with this information in another way. Is there a more idiomatic way of achieving this that I don't know about?

like image 666
SCdF Avatar asked Jun 12 '13 03:06

SCdF


2 Answers

There's nothing magic about $.each(). It's a simple function, and either it does what you need or it doesn't. If it doesn't, there's nothing wrong with setting variables as in your code example. And there's nothing wrong with writing your own each() function that does exactly what you need. It's just a few lines of code that runs a loop and calls your callback function.

Here's the source code for $.each() in jQuery 1.10.1:

// args is for internal usage only
each: function( obj, callback, args ) {
    var value,
        i = 0,
        length = obj.length,
        isArray = isArraylike( obj );

    if ( args ) {
        if ( isArray ) {
            for ( ; i < length; i++ ) {
                value = callback.apply( obj[ i ], args );

                if ( value === false ) {
                    break;
                }
            }
        } else {
            for ( i in obj ) {
                value = callback.apply( obj[ i ], args );

                if ( value === false ) {
                    break;
                }
            }
        }

    // A special, fast, case for the most common use of each
    } else {
        if ( isArray ) {
            for ( ; i < length; i++ ) {
                value = callback.call( obj[ i ], i, obj[ i ] );

                if ( value === false ) {
                    break;
                }
            }
        } else {
            for ( i in obj ) {
                value = callback.call( obj[ i ], i, obj[ i ] );

                if ( value === false ) {
                    break;
                }
            }
        }
    }

    return obj;
},

The only complexity in this code comes from the fact that it handles both objects and arrays, with two different cases for each of those.

For the case you're dealing with, it boils down to:

        for ( ; i < length; i++ ) {
            value = callback.call( obj[ i ], i, obj[ i ] );

            if ( value === false ) {
                break;
            }
        }

There's just not that much code there, and if doesn't do what you want, you can easily write your own similar iterator that does exactly what you need.

You could even use a simple for loop.

function doExcitingStuff() {
    var $elements = $("some-selector-logic");
    for( var i = 0;  i < $elements.length;  i++ ) {
        var $element = $($elements[i]);
        if( someLogic($element) )
            return;
        // Otherwise do stuff related to $element
    }
    someMoreExcitingCode();  // only runs if the loop completes
}

It really all depends on what makes your code the cleanest. You can either use $.each() or write your own.

like image 39
Michael Geary Avatar answered Sep 29 '22 10:09

Michael Geary


Because of the way jQuery functions tend to return the same collection of objects they were called on (to allow chaining), there's no way for the each() function to return that information to you - it's already returning a jQuery object that you can chain other method calls onto.

Based on the information you've provided I don't see a problem with using an external flag or counter variable to get this information. If you tell us a bit more about why you need to know this though, we may be able to provide more useful suggestions.

like image 58
jcsanyi Avatar answered Sep 29 '22 08:09

jcsanyi