jQuery: Understanding the "chain"

Categories: Source Code, jQuery, JavaScript

Yesterday I was writing some jQuery code and I thought I came across a bug—until I realized it was a bug in my way of thinking and not with jQuery.

What I was trying to do is to dynamically append to the body tag some html that looked like this:

<div>
    outer
    <div>
        inner
    </div>
</div>

The trick was I wanted jQuery to return a reference to the "inner" <div> element.

Knowing that that I could easily append a <div> to the <body> tag by using: $("<div>inner</div>").appendTo("body");, I thought I could just write the following and all would be well:

$("<div>inner</div>")
    .appendTo("<div>outer</div>")
    .appendTo("body");

From looking at the code quickly everything looked ok, but only the <div>inner</div> was being appended to the DOM. This had me confused. I knew that the <div>inner</div> was being inserted into the <div>outer</div>, but why was I not getting the outer <div> in the DOM?

That's when I realized the mistake I made. It's all about the jQuery "chain".

One of the coolest features of jQuery is the ability to "chain" commands together. This allows you to attach multiple commands together with one line of code.

The problem I was having is that I was originally thinking the appendTo() method was changing the jQuery chain to refer to the element that I just appended to. This is incorrect. jQuery always references the first element in the chain, unless you use a command that explicitly changes the chain.

This allows you to write a piece of code that appends an element to multiple elements on a page. The following code would put the string "append me!" in bold print to the end of each <div> and <p> element on the page:

$("<strong>append me!</strong>")
    .appendTo("div")
    .appendTo("p");

So now that I realized my problem, how could I solve my problem? That part is actually quite simple. I just need to call the parent() method after appending to the <div>outer</div>. This changes the jQuery chain so that it's now referencing the outer <div> instead of the inner <div>.

// create the inner div
var $inner = $("<div>inner</div>")
    // append it to a new outer div
    .appendTo("<div>outer</div>")
    // next change the jQuery chain to the "outer" div
    .parent()
        // append the outer div to the body
        .appendTo("body")
    // finally, go back to the last destructive command,
    // giving us back a pointer to the "inner" div
    .end();

The end() method is important in my case because I want the variable $inner to be a reference to the first <div> I created. Calling the end() method at the end of the chain, makes jQuery go back to what it was referencing before the parent() method was called. I've used indenting to illustrate when a destructive call has been made.

Comments

Rick Faircloth's Gravatar Thanks for the mini-tutorial on chaining. I'm still learning jQuery and its syntax, so this was very helpful, without being overwhelming!

Rick
Nicolas Hoizey's Gravatar Thanks a lot for this useful tip!
jurciks's Gravatar wouldnt it be $("div > div") ?
Dan G. Switzer, II's Gravatar @Jurciks:

The $("div > div") selector would select any div element that is a direct child of a parent div.

However, in my case I'm dynamically generating the div content. In order to use that as a selector, I'd have to store the contents in a variable and then use that selector on the context.

My goal was to do this without having to create another temporary variable--which is why I'm using jQuery's chaining. You could technically do a .find("div > div") after the .appendTo("body") command instead of using the .end() command, but that would be extra unnecessary overhead. The end() function will get you the same command without having to do any DOM parsing.
Krishna Chaitanya's Gravatar Thank you so much!
Perfect example indeed :)
Dan G. Switzer, II's Gravatar @Krishna:

One thing to keep in mind, is that I believe this behavior was changed in jQuery v1.3.x. I believe now the appendTo() changes the chain to refer to the new appended item.

I need to test this and update this example if true--I just haven't had a whole lot of time to dabble in v1.3.
Dexter's Gravatar appendTo("body") was something i was badly looking for!

Thanks!
Eric Larson's Gravatar I realize this is a little late, but it seems like your scenario provides a good example of when *not* to use chaining:

var outer = $('<div>outer</div>');
var inner = $('<div>inner</div>');
outer.append(inner);
$('body').append(outer);

Again, I know refactoring like this is not the point, but figured it can't hurt to point out that chaining in jQuery, while slick, is not always the best approach.
john's Gravatar why not use .wrap and then append to dom?
Damien Ansart's Gravatar I still use a kind of chaining for this...

$("<div>inner</div>")
  .appendTo($("<div>outer</div>")
    .appendTo("body"));

check the ( ) :
Create a <div>outer</div> and append this to the body, then the outer div is returned, and passed as parameter for the newly created <div>inner</div>'s appendTo.
Easy.
Andres Leiva's Gravatar Hi...
The example:
$("<strong>append me!</strong>").appendTo("div").appendTo("p");

Does not work for me... JQuery alway append the text to "p" only...
Damien's Gravatar Andres Leiva :
It's normal : You create a strong, so this strong is selected.
When you do appendTo('div'); the strong is selected and appended to the div, and then the strong is returned !!
To do what you need, use this instead :
$("<strong>append me!</strong>").appendTo( $("div").appendTo("p") );
in this case : select the div , append it to the p, the p is returned inside the ( ) of the strong's appendTo.
Damien's Gravatar Whoops, i made a mistake , i meant the div is returned inside the ( )

Add Comment

Leave this field empty


If you subscribe, any new posts to this thread will be sent to your email address.