Using .each() and contexts

One really cool usage of .each is using it to verify existence of a block of html on the page. If you are including all your site's javascript on every page or have DOM blocks that are conditionally included, then using this technique can help to improve performance significantly and compartmentalize your code.

This is probably how you typically see jQuery code written:

$(function() {

  $("#my_block select").change(function() {
    ...
  });

  $("#my_block a:contains('edit')".click(function() {
    ...
  });
});

Both of those selectors are invoked on every page load regardless of whether #my_block even exists. Also, jQuery can't use getElementById to look up #my_block because the selectors are combined with other criteria.

Instead of repeating the selector in multiple calls, we can use .each to guard the entire section from running only if the block actually exists.

$(function() {
  $("#my_block").each(function() {
    $("#my_block select").change(function() {
      ...
    });

    $("#my_block a:contains('edit')").click(function() {
      ...
    });
  });
});

In this example, we are now able to use the id of the block by itself, which allows jQuery to use the browser's built in and very fast getElementById function to look up the block. It also prevents any of the other selectors from running if the block does not exist.

Writing it this way also gives us another benefit. We now have the raw DOM object of the block available to use as a context for other searches. In case you aren't familiar with contexts in jQuery, it allows you to pass in a raw DOM object (not a jQuery object!) to use as the root node for the search. Typically when you run a selector, it searches the entire document for matches. Providing a context will restrict the search to the node you pass in. Be careful though, because if the context doesn't exist or is null then it will search the entire document tree.

Contexts work well with .each because we are guaranteed that it will actually exist. If nothing matches the selector, then the anonymous function inside will not fire. Putting it all together would look something like this.

$(function() {
  $("#my_block").each(function() {
    var my_block = this;

    $("select", my_block).change(function() {
      ...
    });

    $("a:contains('edit')", my_block).click(function() {
      ...
    });
  });
});

Once more nice thing about this is that it leaves you poised to move the code into an application specific jQuery plugin. You can basically take the content of it out and call it on your block.

(function($) {
  $.fn.extend({
    myBlockBehavior: function() {
      return this.each(function() {
        var my_block = this;

        $("select", my_block).change(function() {
          ...
        });

        $("a:contains('edit')", my_block).click(function() {
          ...
        });
      });
    }
  });
})(jQuery);

$(function() {
  $("#my_block").myBlockBehavior();
});

And now you have some clean, fast, encapsulated code that you can easily reuse.