Telerik blogs

I'm sure most of you can relate to that feeling you get when you learn about something that you're pretty sure everyone else already knows about. Something fundamental. I remember when I learned that I could test for nullable integers in C# by using hasValue. Or when I first learned that you could use array notation in JavaScript to dynamically access properties and methods. Sometimes we don't talk about these things. We sort of just look around to make sure no one was watching when we were the last to find out.

I love these moments. Everything we now know has to be learned sometime. So enough pontification, let me tell you about my latest revelation. Make sure nobody is looking just in case I was the second to last to figure this out. Actually, call everyone and tell them. I can guarantee you that you are not alone.

Find()ing An Element

Of course the core beauty and even namesake of jQuery is the ability to select any item from the DOM and read/write/mutate it with ease. This blessing is also a curse. The multiple ways to find an element, coupled with convenience of chaining, is a breeding ground for code you are going to regret.

For example, assume that you want to select the 'content' div from the 'post' div which has a click event on it. In the click event, you will get the post itself. Then you need to change some colors and toggle a few classes for the content and title elements.

Sample HTML Fragment

<div class="post">
  <div class="post-title">
    <h2>Test Post</h2>
    <div class="post-subheading">This is a test post</div>
  </div>
  <div class="post-content">Click anywhere in the box to change the color of the title and the background of the text. Click it again to remove the coloration.</div>
  </div>
</div>

Binding frameworks like Kendo UI MVVM and KnockoutJS not withstanding, how do you do this? The way I would do it is to attach an event handler to the container (in this case the body), use the find() method to get the elements that I need to work with, and then toggle their styles.

Changing Styles On Click

(function() {

  $(document).on("click", ".post", function(e) {  

    // get a reference to the post
    var post = $(this);

    // toggle the styles
    post.find(".post-content").toggleClass("background");
    post.find(".post-title").find("h2").toggleClass("title");

  });

}());

Here is my implementation:

This works and is how I have been writing jQuery for years. I use binding frameworks when I can because I like declarative syntax and I don't really like to manually select and mutate the DOM. However, there are times when you have no choice. But is this really the best way to do this? This is a really simple example and more real life implementations will contain calls to parent(), children(), closest(), end() and the like. I have jQuery selector skeletons in my closet that would cause John Resig himself to curl up into the fetal position and sob.

Find() A Better Way

Brandon Satrom has a talk that he does called "The Secrets Of Awesome JavaScript API Design". There is an article that covers the same content and I highly recommend that you read it. It talks about architectural concepts like consistency, balance and emphasis and how they apply not only to good visual design, but to excellent API design as well.

He has a slide in that presentation that goes over all of the different overloads for jQuery.

There's a lot on that slide (and that's the point in the presentation), but the top example jumped out at me. What is that second parameter?

I know I have seen this many times before. How can you miss it? It's right in the documentation.  However, it never really clicked with me why I would ever use this.  Thanks to this presentation, I thought about it and used it in a real world application.  Then it all came together.

What You Would Not Do

You wouldn't do this right?

What You Would Not Do

$(document).find("#someId").show();

And why not? Because it's entirely unnecessary. The context is already the document so you would just select someId by it's ID and then show it.

What You Would Do

Instead, you would just do this...

What You Would Do

$("#someId").show();

The same logic applies when you factor in that second optional "context" parameter. It limits the scope of the search to the element that you specify. Let's look at the first example again. This time instead of find(), we'll specify the context.

Using Context Overload In Lieu Of Find

$(function() {

  $(document).on("click", ".post", function(e) {  
    var post = $(this);

    $(".post-content", post).toggleClass("background");
    $("h2", post).toggleClass("title");

  });

});

That looks a whole lot cleaner to me. It does not require me to use any additional jQuery methods, and therefor cleans up the code and reduces chaining.

This was a revelation for me.  I've seen this all before, but why did it not click until now? How has something so obvious completely eluded my attention? Why does my seven year old put his clothes on backward every single day? These are the questions I was asking myself.

What Is Find() Actually Doing

I immediately posited that find() was actually doing the same thing under the covers. Simply setting the context of the selector and calling the root jQuery function. I cracked open the jQuery source to find() out for myself.

jQuery Find Method

find: function( selector ) {
  var i,
    ret = [],
    self = this,
    len = self.length;

  if ( typeof selector !== "string" ) {
    return this.pushStack( jQuery( selector ).filter(function() {
      for ( i = 0; i < len; i++ ) {
        if ( jQuery.contains( self[ i ], this ) ) {
          return true;
        }
      }
    }) );
  }

  for ( i = 0; i < len; i++ ) {
    jQuery.find( selector, self[ i ], ret );
  }

  // Needed because $( selector, context ) becomes $( context ).find( selector )
  ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
  ret.selector = this.selector ? this.selector + " " + selector : selector;
  return ret;
}

Notice the very last comment? "Needed because $(selector, context) becomes $(context).find(selector)". My hypothesis was completely backwards. I was WAY off!

So setting the context is apparently calling find() for you under the covers. It's sugar and I like it. I realize that find() is slightly faster since you're skipping the step of making jQuery translate the context to the find. The margin is tiny though and I find it hard to believe that it would ever make a bit of difference for your application. Writing less code on the other hand, makes all the difference in the world.

Is find() Bad

Absolutely not! In fact, many would argue that it's produces more readable code.  Elijah Manor, who made several tweaks to this article, points out that it reads from left to right where the context overload is really right to left.  I think that using the context in a one level search works just fine.  However, if you ended up going several levels down, it would be quite unreadable to try and handle that all in the context overload.

It's just good to know all of the terse shortcuts that you have available in your jQuery Toolbox.

Learning New Tricks

Now is a good time for you to head over and download Kendo UI so you can put your new found find/context knowledge to work in a real world application. I'm going to go back to working on my project for next week which, coincidentally, has far fewer find statements than it did 2 weeks ago.


Burke Holland is the Director of Developer Relations at Telerik
About the Author

Burke Holland

Burke Holland is a web developer living in Nashville, TN and was the Director of Developer Relations at Progress. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke worked for Progress as a Developer Advocate focusing on Kendo UI.

Comments

Comments are disabled in preview mode.