jQuery Selector Perf - Right-to-Left

JavaScript performance comparison

Revision 123 of this test case created by void

Info

Based on rev. 118; added a couple of tests and sorted them by performance (on my computer/browser); note .children() ~= In context

Preparation code

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<div style="position:absolute; width: 800px; left: -2000px;">
<div class="wrapper">
  <div id="header">
    <div id="branding">
      <h1><a href="http://www.LearningjQuery.com/">Learning jQuery</a></h1>
      <div class="description">Tips, techniques, and tutorials for the jQuery JavaScript library</div>
    </div>

    <ul id="nav-main" class="hnav">
      <li class="page-item page-item-home"><a href="/">Home</a></li>
          <li class="page-item page-item-107"><a href="/categories">Categories</a></li>
    <li class="page-item page-item-106"><a href="/archives">Archives</a></li>
    <li class="page-item page-item-2"><a href="/about">About</a></li>
    <li class="page-item page-item-63"><a href="/contact">Contact</a></li>
      <li class="last"><a class="replace rss" href="http://www.learningjquery.com/feed">Entries RSS</a></li>
    </ul>

    <ul id="nav-filter" class="hnav">
      <li>Filter:</li>
      <li class="cat-item cat-item-levels"><a href="/category/levels">All</a></li>
        <li class="cat-item cat-item-5"><a href="http://www.LearningjQuery.com/category/levels/beginner" title="View all posts filed under Beginner">Beginner</a>
</li>
  <li class="cat-item cat-item-6"><a href="http://www.LearningjQuery.com/category/levels/intermediate" title="View all posts filed under Intermediate">Intermediate</a>
</li>
  <li class="cat-item cat-item-7"><a href="http://www.LearningjQuery.com/category/levels/advanced" title="View all posts filed under Advanced">Advanced</a>
</li>
      <li><form method="get" id="searchform" action="http://www.LearningjQuery.com/">
<label>Search</label>
<div><input type="text" value="" name="s" id="s" />
<input type="image" src="http://www.learningjquery.com/wp-content/themes/ljq/images/search.png" id="searchsubmit" value="go" />
</div>
</form>
</li>
    </ul>
  </div>
    <div class="before page-nav">
      <div class="prev"><a href="http://www.LearningjQuery.com/2011/09/using-jquerys-data-apis" rel="prev">Using jQuery&#8217;s Data APIs</a></div>
      <div class="next"></div>
    </div>
  <hr />
  <div id="content">
<div id="box">
  <div> Hi </div>
  <p> Hi </p>
  <p> Hi </p>
  <p> Hi </p>
  <div> Hi </div>
  <p> Hi </p>
  <p> Hi </p>
  <p> Hi </p>
  <p> Hi </p>
  <div> Hi </div>
  <p> Hi </p>
  <p> Hi </p>
  <p> Hi </p>
  <div> Hi </div>
</div>
<div  class="contents">

      <div class="hentry">
      <abbr class="postdate published" title="2011-12-20T14:51:07+00:00">
        <span class="month m-12">Dec</span>
        <span class="day d-20">20</span>
        <span class="year y-2011">2011</span>
      </abbr>

      <div class="post" id="post-1324">
        <h2><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods" rel="bookmark" title="Permanent Link: Using jQuery&#8217;s .pushStack() for reusable DOM traversing methods">Using jQuery&#8217;s .pushStack() for reusable DOM traversing methods</a></h2>

                  <span id="comments-title-link">read <a href="#comments-title">5 comments</a></span>
       
        <div class="author">by <a href="http://www.learningjquery.com/contact/">Karl Swedberg</a></div>
        <div class="entrytext">
          <p>The <code>.pushStack()</code> method has been in jQuery since before version 1.0, but it hasn't received a whole lot of attention outside of core developers and plugin authors. While its usefulness may not be immediately apparent, it can come in really handy in some situations, so I'd like to take a quick look at what it does, how it works, and how we can use it.</p>

  <h4>pushStack Basics </h4>
  <p>At its most basic level, the <code>.pushStack()</code> method accepts an array of DOM elements and "pushes" it onto a "stack" so that later calls to methods like <code>.end()</code> and <code>.andSelf()</code> behave correctly. (Side note: As of jQuery 1.4.2, you can pass in a jQuery object instead of an array, but that isn't documented and jQuery itself always uses an array, so that's what we'll stick to here.)</p>
  <p>Internally, jQuery uses <code>.pushStack()</code> to keep track of the previous jQuery collections as you chain <a href="http://api.jquery.com/category/traversing/">traversing methods</a> such as <code>.parents()</code> and <code>.filter()</code>. This lets us traverse through the DOM, do some stuff, "back up" to previous collections within the same chain using <code>.end()</code>, and then do something else. Here is a somewhat contrived example:</p>
<span id="more-1324"></span>
<div class="igBar"><span id="ljavascript-1"><a href="#" onclick="javascript:showPlainTxt('javascript-1'); return false;">PLAIN TEXT</a></span></div><div class="syntax_hilite"><span class="langName">JavaScript:</span><br /><div id="javascript-1">
<div class="javascript" style="font-family:monospace;"><ol><li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// select some divs</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'div.container'</span><span style="color: #009900;">&#41;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; <span style="color: #006600; font-style: italic;">// find some spans inside those divs and add a class to them</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; .<a href="/wp-content/themes/ljq/docs-1.7.php?fn=find"><span style="">find</span></a><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'span'</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=addClass"><span style="">addClass</span></a><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'baby'</span><span style="color: #009900;">&#41;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// pop those spans off the &quot;stack&quot;,</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// returning to the previous collection (div.container)</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=end"><span style="">end</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; <span style="color: #006600; font-style: italic;">// add a class to the parent of each div.container</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; .<a href="/wp-content/themes/ljq/docs-1.7.php?fn=parent"><span style="">parent</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=addClass"><span style="">addClass</span></a><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'daddy'</span><span style="color: #009900;">&#41;</span>;</div></li>
</ol></div>
</div></div><br />

<p>Because <code>.find()</code> returns the result of a <code>.pushStack()</code> call to keep track of the previous collection (as does <code>.parent()</code>), we can use <code>.end()</code> in the above example to return to the container divs.</p>

<h4>Using pushStack for Fun and Profit</h4>
<p>So, this is great for jQuery, but what can <code>.pushStack()</code> do for me and my code? Well, it can help me write specialized DOM traversal plugins that act just like jQuery's own traversal methods. In other words, I can stop chaining the same sets of traversal methods together and instead write a reusable function that still works with with <code>.end()</code> and all that. For example, let's say I often have a need to find an element's grandparent. While I could write <code>$('#myElement').parent().parent()</code> every time, it might be nice to just be able to write <code>$('#myElement').grandparent()</code> instead. A naïve way to write a grandparent plugin would look like this (changing the method name to "grandpa" for this example): </p>

<div class="igBar"><span id="ljavascript-2"><a href="#" onclick="javascript:showPlainTxt('javascript-2'); return false;">PLAIN TEXT</a></span></div><div class="syntax_hilite"><span class="langName">JavaScript:</span><br /><div id="javascript-2">
<div class="javascript" style="font-family:monospace;"><ol><li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// NOT recommended!</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; $.<span style="color: #660066;">fn</span>.<span style="color: #660066;">grandpa</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">this</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=parents"><span style="">parents</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=parents"><span style="">parents</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; <span style="color: #009900;">&#125;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>jQuery<span style="color: #009900;">&#41;</span>;</div></li>
</ol></div>
</div></div><br />

<p>The problem here is that <em>two</em> new jQuery object instances are added to the stack. So, let's see what happens when we use it:  </p>
<div class="igBar"><span id="ljavascript-3"><a href="#" onclick="javascript:showPlainTxt('javascript-3'); return false;">PLAIN TEXT</a></span></div><div class="syntax_hilite"><span class="langName">JavaScript:</span><br /><div id="javascript-3">
<div class="javascript" style="font-family:monospace;"><ol><li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// The DOM looks like this:</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &lt;div class=&quot;grandpa&quot;&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &nbsp;&lt;div class=&quot;pa&quot;&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &nbsp; &nbsp;&lt;div class=&quot;child son&quot;&gt;&lt;/div&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &nbsp;&lt;/div&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &lt;/div&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #003366; font-weight: bold;">var</span> elem <span style="color: #339933;">=</span> $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'div.son'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">grandpa</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=end"><span style="">end</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'div.son'</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=text"><span style="">text</span></a><span style="color: #009900;">&#40;</span> elem.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=attr"><span style="">attr</span></a><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'class'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span>;</div></li>
</ol></div>
</div></div><br />

<div class="grandpa">
 <div class="pa">
   <div class="child son"></div>
 </div>
</div>

<p>Without seeing the plugin, we would expect to see <em>"child son"</em> inserted into <code>&lt;div class="son"&gt;</code>, but <em>"pa"</em> is inserted instead. Each <code>.parent()</code> call in the plugin adds to the stack, so using <code>.end()</code> only pops the second one off.</p>
<p>If we use <code>.pushStack()</code> instead, however, we can achieve the expected behavior:</p>

<div class="igBar"><span id="ljavascript-4"><a href="#" onclick="javascript:showPlainTxt('javascript-4'); return false;">PLAIN TEXT</a></span></div><div class="syntax_hilite"><span class="langName">JavaScript:</span><br /><div id="javascript-4">
<div class="javascript" style="font-family:monospace;"><ol><li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; $.<span style="color: #660066;">fn</span>.<span style="color: #660066;">grandma</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> els <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=parent"><span style="">parent</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=parent"><span style="">parent</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">this</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=pushStack"><span style="">pushStack</span></a><span style="color: #009900;">&#40;</span> els.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=get"><span style="">get</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; <span style="color: #009900;">&#125;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>jQuery<span style="color: #009900;">&#41;</span>;</div></li>
</ol></div>
</div></div><br />

<p>Within a plugin function, one that is a method of<code> $.fn</code>, the <code>this</code> keyword refers to the jQuery object; therefore, the <code>els</code> variable refers to a jQuery object, as well. To convert it to an array, we use jQuery's <code>.get()</code> method, and we pass that array to <code>.pushStack()</code>. Let's see if <code>.grandma()</code> works any better than <code>.grandpa()</code>.</p>
<div class="igBar"><span id="ljavascript-5"><a href="#" onclick="javascript:showPlainTxt('javascript-5'); return false;">PLAIN TEXT</a></span></div><div class="syntax_hilite"><span class="langName">JavaScript:</span><br /><div id="javascript-5">
<div class="javascript" style="font-family:monospace;"><ol><li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// The DOM looks like this:</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &lt;div class=&quot;grandma&quot;&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &nbsp;&lt;div class=&quot;ma&quot;&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &nbsp; &nbsp;&lt;div class=&quot;child daughter&quot;&gt;&lt;/div&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &nbsp;&lt;/div&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #006600; font-style: italic;">// &lt;/div&gt;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #003366; font-weight: bold;">var</span> elem <span style="color: #339933;">=</span> $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'div.daughter'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">grandma</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=end"><span style="">end</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'div.daughter'</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=text"><span style="">text</span></a><span style="color: #009900;">&#40;</span> elem.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=attr"><span style="">attr</span></a><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'class'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span>;</div></li>
</ol></div>
</div></div><br />

<div class="grandma">
 <div class="ma">
   <div class="child daughter"></div>
 </div>
</div>

<p>Here, "child daughter" is inserted, which means that <code>.end()</code> works as expected, changing the jQuery collection from the result of <code>.grandma()</code> to the result of <code>$('div.daughter')</code>. So, we've just successfully written a DOM traversal plugin, albeit a very simple one.</p>

<h4>The Simplest DOM Traversal Methods </h4>
<p>If the plugin only uses one DOM traversal method, then <code>.pushStack()</code> isn't really necessary. The <a href="http://www.elijahmanor.com/2011/07/filterbydata-jquery-plugin.html">HTML5 data filter plugin</a> written by Elijah Manor illustrates this point nicely: </p>

<div class="igBar"><span id="ljavascript-6"><a href="#" onclick="javascript:showPlainTxt('javascript-6'); return false;">PLAIN TEXT</a></span></div><div class="syntax_hilite"><span class="langName">JavaScript:</span><br /><div id="javascript-6">
<div class="javascript" style="font-family:monospace;"><ol><li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; $.<span style="color: #660066;">fn</span>.<span style="color: #660066;">filterByData</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span> type<span style="color: #339933;">,</span> value <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">this</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=filter"><span style="">filter</span></a><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> value <span style="color: #339933;">!=</span> <span style="color: #003366; font-weight: bold;">null</span> <span style="color: #339933;">?</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; &nbsp; &nbsp; $<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=data"><span style="">data</span></a><span style="color: #009900;">&#40;</span> type <span style="color: #009900;">&#41;</span> <span style="color: #339933;">===</span> value <span style="color: #339933;">:</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; &nbsp; &nbsp; $<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=data"><span style="">data</span></a><span style="color: #009900;">&#40;</span> type <span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #003366; font-weight: bold;">null</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; <span style="color: #009900;">&#125;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>jQuery<span style="color: #009900;">&#41;</span>;</div></li>
</ol></div>
</div></div><br />

<p>Only one new jQuery collection is added to the stack, via <code>.filter()</code>, so using <code>.end()</code> simply pops that one off, and our job is done. </p>

<h4>Filtering grandparents</h4>
<p>For the sake of completeness, it would be nice for this DOM traversal plugin to allow optional "filtering" of the parent and grandparent elements. After all, jQuery's <code>.parent()</code>  and <code>.parents()</code> allow filtering. For example, if I were to write <code>$('div.child').parent('.daddy')</code>, the jQuery collection would only contain an element if <code>div.child</code> had a parent element and if that parent had a class of "daddy."</p>
<p>There are plenty of reasonable ways one could include the filters, but for my purposes I'm going to have a <code>.grandparent()</code> method <em>optionally</em> accept two arguments. If only one argument is provided, it will filter the <em>grandparent</em> element only; if two are provided, the first will filter the parent and the second will filter the grandparent. Here is the full plugin plugin: </p>

<div class="igBar"><span id="ljavascript-7"><a href="#" onclick="javascript:showPlainTxt('javascript-7'); return false;">PLAIN TEXT</a></span></div><div class="syntax_hilite"><span class="langName">JavaScript:</span><br /><div id="javascript-7">
<div class="javascript" style="font-family:monospace;"><ol><li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; $.<span style="color: #660066;">fn</span>.<span style="color: #660066;">grandparent</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span> parentFilter<span style="color: #339933;">,</span> grandFilter <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span> <span style="color: #339933;">!</span>grandFilter <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; &nbsp; grandFilter <span style="color: #339933;">=</span> parentFilter;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; &nbsp; parentFilter <span style="color: #339933;">=</span> undefined;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> els <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=parent"><span style="">parent</span></a><span style="color: #009900;">&#40;</span> parentFilter <span style="color: #009900;">&#41;</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=parent"><span style="">parent</span></a><span style="color: #009900;">&#40;</span> grandFilter <span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">this</span>.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=pushStack"><span style="">pushStack</span></a><span style="color: #009900;">&#40;</span> els.<a href="/wp-content/themes/ljq/docs-1.7.php?fn=get"><span style="">get</span></a><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;">&nbsp; <span style="color: #009900;">&#125;</span>;</div></li>
<li style="font-weight: normal; vertical-align:top;color:#ACAA9A;"><div style="background-color: transparent;"><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>jQuery<span style="color: #009900;">&#41;</span>;</div></li>
</ol></div>
</div></div><br />

<p>Finally, we have a nice <code>.grandparent()</code> plugin that adheres to the contract set by other jQuery DOM traversal methods—one that works with both filters and the <code>.end()</code> method. Here is what it could look like in use.</p>

<iframe style="width: 100%; height: 400px" src="http://jsfiddle.net/kswedberg/JSDdb/embedded/js,html,result" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
          <div id="scripts" class="scripts"><h4>Scripts included in this post:</h4><ul><li><a href="/js/jquery.grandparent.js">http://www.learningjquery.com/js/jquery.grandparent.js</a></li><li><a href="/js/grandpa.js">http://www.learningjquery.com/js/grandpa.js</a></li><li><a href="/js/grandma.js">http://www.learningjquery.com/js/grandma.js</a></li><li><a href="/scripts-used-on-this-site/">Other JavaScript Files &hellip;</a></li></ul></div>                 </div>
      </div>
    </div>

  <div id="comments">

      <span title="comment feed for Using jQuery&#8217;s .pushStack() for reusable DOM traversing methods" class="rss replace"><a href='http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/feed'>comment feed</a></span>
    <h3 id="comments-title">5 comments    </h3>
    <div class="navigation group">
      <div class="alignleft"></div>
      <div class="alignright"></div>
    </div>

    <ol class="commentlist">
         <li class="comment even thread-even depth-1" id="comment-85324">
     <div class="comment-single" id="div-comment-85324">
      <div class="comment-author vcard">
                  <cite class="fn"><a href='http://dreepi.com' rel='external nofollow' class='url'>Esteban</a></cite>      </div>
      <div class="comment-meta commentmetadata"><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/comment-page-1#comment-85324">December 23, 2011 at 10:33 pm</a></div>
      <div class="comment-body">

                  <p>Any comments on .end() vs caching selectors? (eg: parent = $("#parent") )</p>
<p>Best practice? Performance improvement?</p>
          <div class="reply">
             <a rel="nofollow" href="/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods?replytocom=85324#respond">Reply</a>          </div>
       
      </div>
    </div>
</li>
   <li class="comment odd alt thread-odd thread-alt depth-1" id="comment-85775">
     <div class="comment-single" id="div-comment-85775">
      <div class="comment-author vcard">
                  <cite class="fn"><a href='http://weavingcode.blogspot.com/' rel='external nofollow' class='url'>Dan Shultz</a></cite>      </div>
      <div class="comment-meta commentmetadata"><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/comment-page-1#comment-85775">December 30, 2011 at 1:37 pm</a></div>
      <div class="comment-body">

                  <p>This is an awesome post - thanks for sharing the info on pushStack() as this can be used to help performance when writing code that does heavy dom manipulation. </p>
<p>@Esteban - without digging too deep, this would be just as performant as "caching" selectors because the essentially that is what pushStack is doing. end() just pops the stack.</p>
<p>Regarding best practices, I would say, don't get too carried away and have someone have to read through a nested chain with 15 end statements in it and I would say, it's a good practice to use pushStack when writing functions as the jquery team has put this method in place most likely for performance reasons and to ensure that you can easily get back to your parent elements. This allows you to traverse and manipulate the dom based on convention instead of configuration also.</p>
<p>Just my 2 cents...</p>
          <div class="reply">
             <a rel="nofollow" href="/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods?replytocom=85775#respond">Reply</a>          </div>
       
      </div>
    </div>
</li>
   <li class="comment even thread-even depth-1" id="comment-86063">
     <div class="comment-single" id="div-comment-86063">
      <div class="comment-author vcard">
                  <cite class="fn"><a href='http://dreepi.com' rel='external nofollow' class='url'>Esteban</a></cite>      </div>
      <div class="comment-meta commentmetadata"><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/comment-page-1#comment-86063">January 4, 2012 at 8:25 am</a></div>
      <div class="comment-body">

                  <p>@Dan thanks for your reply!<br />
After seeing this post I created a jsperf test to see if there was any difference in performance:<br />
<a href="http://jsperf.com/jquery-end-vs-cached-selector" rel="nofollow">http://jsperf.com/jquery-end-vs-cached-selector</a><br />
only ran it on Chrome and the seem to perform the same.<br />
But I agree on the point of not chaining 15 end() calls. I haven't changed any code yet to use end(), but it's really good to know it exists</p>
          <div class="reply">
             <a rel="nofollow" href="/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods?replytocom=86063#respond">Reply</a>          </div>
       
      </div>
    </div>
</li>
   <li class="comment odd alt thread-odd thread-alt depth-1" id="comment-86869">
     <div class="comment-single" id="div-comment-86869">
      <div class="comment-author vcard">
                  <cite class="fn"><a href='http://www.fromdev.com' rel='external nofollow' class='url'>jQuery Developer</a></cite>      </div>
      <div class="comment-meta commentmetadata"><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/comment-page-1#comment-86869">June 18, 2012 at 1:54 pm</a></div>
      <div class="comment-body">

                  <p>Awesome, I guess I can improve my current implementation by using this technique. This method is surely under utilized. Thanks for detailed explanation.</p>
          <div class="reply">
             <a rel="nofollow" href="/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods?replytocom=86869#respond">Reply</a>          </div>
       
      </div>
    </div>
</li>
   <li class="comment even thread-even depth-1" id="comment-87320">
     <div class="comment-single" id="div-comment-87320">
      <div class="comment-author vcard">
                  <cite class="fn"><a href='http://eacsoft.com/' rel='external nofollow' class='url'>Lexi</a></cite>      </div>
      <div class="comment-meta commentmetadata"><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/comment-page-1#comment-87320">October 19, 2012 at 7:02 am</a></div>
      <div class="comment-body">

                  <p>Using <code>.pushStack()</code> is so confusing to me, this article helps a lot! Thanks for the tips, I'm gonna start applying a few of them for my new project.</p>
          <div class="reply">
             <a rel="nofollow" href="/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods?replytocom=87320#respond">Reply</a>          </div>
       
      </div>
    </div>
</li>
    </ol>

    <div class="navigation group">
      <div class="alignleft"></div>
      <div class="alignright"></div>
    </div>

        <h3 id="pings-title">2 Pings</h3>
    <ol class="commentlist">
       
   <li class="pingback even thread-even depth-1" id="comment-85996">
     <div class="comment-single" id="div-comment-85996">
      <div class="comment-author vcard">
         <cite class="fn"><a href='http://labnotes.org/2012/01/03/rounder-corners-316-might-build/' rel='external nofollow' class='url'>Rounder Corners 316 – might build /by @assaf</a></cite>      </div>
            <div class="comment-meta commentmetadata"><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/comment-page-1#comment-85996">January 3, 2012 at 12:00 pm</a></div>
      <p>[...] Pushing it It feels like everyday there&#8217;s something new to learn about jQuery. This time, how to use jQuery&#8217;s pushStack. [...]</p>
     </div>
</li>

   <li class="pingback odd alt thread-odd thread-alt depth-1" id="comment-86371">
     <div class="comment-single" id="div-comment-86371">
      <div class="comment-author vcard">
         <cite class="fn"><a href='http://blog.phatograph.com/2012/01/23/using-jquerys-pushstack-for-reusable-dom-traversing-methods/' rel='external nofollow' class='url'>Using jQuery’s .pushStack() for reusable DOM traversing methods | phato.blog`</a></cite>      </div>
            <div class="comment-meta commentmetadata"><a href="http://www.LearningjQuery.com/2011/12/using-jquerys-pushstack-for-reusable-dom-traversing-methods/comment-page-1#comment-86371">January 23, 2012 at 12:55 am</a></div>
      <p>[...] Using jQuery’s .pushStack() for reusable DOM traversing methods [...]</p>
     </div>
</li>
    </ol>
   
 
    <div id="respond">
    <h3>Leave a Comment</h3>

   
      <form action="http://www.learningjquery.com/wp-comments-post.php" method="post" id="commentform">
        <fieldset>
       
          <p>
            <input class="required" type="text" name="author" id="author" value="" size="22" tabindex="1" />
            <label for="author"><strong>Name</strong> <small>(required)</small></label>
          </p>
          <p>
            <input class="required email" type="text" name="email" id="email" value="" size="22" tabindex="2" />
            <label for="email"><strong>Mail</strong> <small>(required, but not published)</small></label>
          </p>
          <p>
            <input class="url" type="text" name="url" id="url" value="" size="22" tabindex="3" />
            <label for="url"><strong>Website</strong></label>
          </p>

                  <div class="instructions">
            <!-- <p><small><strong>XHTML:</strong> You can use these tags: &lt;a href=&quot;&quot; title=&quot;&quot;&gt; &lt;abbr title=&quot;&quot;&gt; &lt;acronym title=&quot;&quot;&gt; &lt;b&gt; &lt;blockquote cite=&quot;&quot;&gt; &lt;cite&gt; &lt;code&gt; &lt;del datetime=&quot;&quot;&gt; &lt;em&gt; &lt;i&gt; &lt;q cite=&quot;&quot;&gt; &lt;strike&gt; &lt;strong&gt; &lt;div align=&quot;&quot; class=&quot;&quot; dir=&quot;&quot; id=&quot;&quot; lang=&quot;&quot; style=&quot;&quot; xml:lang=&quot;&quot;&gt; &lt;embed style=&quot;&quot; type=&quot;&quot; id=&quot;&quot; height=&quot;&quot; width=&quot;&quot; src=&quot;&quot; object=&quot;&quot;&gt; &lt;iframe width=&quot;&quot; height=&quot;&quot; frameborder=&quot;&quot; scrolling=&quot;&quot; marginheight=&quot;&quot; marginwidth=&quot;&quot; src=&quot;&quot;&gt; &lt;img alt=&quot;&quot; align=&quot;&quot; border=&quot;&quot; class=&quot;&quot; height=&quot;&quot; hspace=&quot;&quot; longdesc=&quot;&quot; vspace=&quot;&quot; src=&quot;&quot; style=&quot;&quot; width=&quot;&quot; title=&quot;&quot; usemap=&quot;&quot;&gt; &lt;object style=&quot;&quot; height=&quot;&quot; width=&quot;&quot; param=&quot;&quot; embed=&quot;&quot;&gt; &lt;param name=&quot;&quot; value=&quot;&quot;&gt; &lt;pre style=&quot;&quot; name=&quot;&quot; class=&quot;&quot; lang=&quot;&quot; width=&quot;&quot;&gt; </small></p> -->
            <p><strong>IMPORTANT:</strong></p>
            <ul>
              <li>If you wish to post code examples, please wrap them in <code>&lt;code&gt;</code> tags. </li>
              <li>Multi-line code should be wrapped in <code>&lt;pre&gt;&lt;code&gt; &lt;/code&gt;&lt;/pre&gt;</code> </li>
              <li>Use <code>&amp;lt;</code> instead of <code>&lt;</code> and <code>&amp;gt;</code> instead of <code>&gt;</code> in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.</li>
            </ul>
          </div>
          <div><textarea class="required" name="comment" id="comment" cols="100%" rows="10" tabindex="4"></textarea></div>

          <p id="scomment">
            <input src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/themes/ljq/images/submit-comment.png" name="submitcomment" type="image" id="submitcomment" tabindex="5" value="Submit Comment" />
          <input type='hidden' name='comment_post_ID' value='1324' id='comment_post_ID' />
<input type='hidden' name='comment_parent' id='comment_parent' value='0' />
          </p>
          <p style="display: none;"><input type="hidden" id="akismet_comment_nonce" name="akismet_comment_nonce" value="505a51e210" /></p>        </fieldset>
      </form>
    </div>

      </div>
 
</div>
<div id="sidebar">
  <div id="sidebar-popular">
        <h3>Popular Posts</h3>
        <ul class="menu">
          <li><a href="http://www.learningjquery.com/2011/09/using-jquerys-data-apis">Using jQuery's Data APIs</a></li>
          <li><a href="http://www.learningjquery.com/2011/02/merging-jquery-deferreds-and-animate">Merging jQuery Deferreds and .animate()</a></li>
          <li><a href="http://www.learningjquery.com/2010/06/a-jquery-ui-combobox-under-the-hood">A jQuery UI Combobox Under the Hood</a></li>
          <li><a href="http://www.learningjquery.com/2009/01/quick-tip-prevent-animation-queue-buildup">Quick Tip: Prevent Animation Queue Buildup</a></li>
          <li><a href="http://learningjquery.com/2008/03/working-with-events-part-1">Working with Events, part 1</a></li>
          <li><a href="http://www.learningjquery.com/2007/10/improved-animated-scrolling-script-for-same-page-links">Improved Animated Scrolling Script for Same-Page Links</a></li>
        </ul>
      </div>
  <div id="books">
    <h3>Recommended Book</h3>
    <ul>
      <li>
        <a class="cover" href="http://link.packtpub.com/A6JfFs"><img src="http://www.learningjquery.com/wp-content/themes/ljq/images/ljq3.jpg" alt="Learning jQuery, Third Edition book" /></a>
        <div><a href="http://link.packtpub.com/A6JfFs">Learning jQuery,<br />Third Edition</a></div><div>By Karl Swedberg &amp; <br />Jonathan Chaffer</div>
      </li>
    </ul>
  </div>
  <div id="sidebar-authors">
    <h3>Authors</h3>
    <ul class="menu">
      <li><a href="http://www.LearningjQuery.com/author/karl" title="Posts by Karl Swedberg"><img alt='' src='http://www.gravatar.com/avatar/0f6923fc391653284355a60fb4974e86?s=36&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D36&amp;r=G' class='avatar avatar-36' height='36' width='36' /><span>70 posts</span>
Karl Swedberg</a></li>

<li><a href="http://www.LearningjQuery.com/author/brandon" title="Posts by Brandon Aaron"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/baaron.jpg" /><span>5 posts</span>
Brandon Aaron</a></li>

<li><a href="http://www.LearningjQuery.com/author/seasoup" title="Posts by Josh Powell"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/jpowell.jpg" /><span>3 posts</span>
Josh Powell</a></li>

<li><a href="http://www.LearningjQuery.com/author/malsup" title="Posts by Mike Alsup"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/malsup.png" /><span>3 posts</span>
Mike Alsup</a></li>

<li><a href="http://www.LearningjQuery.com/author/jboesch" title="Posts by Jordan Boesch"><img alt='' src='http://www.gravatar.com/avatar/f8fc60d4fbf1454f037c2750660a84d0?s=36&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D36&amp;r=G' class='avatar avatar-36' height='36' width='36' /><span>2 posts</span>
Jordan Boesch</a></li>

<li><a href="http://www.LearningjQuery.com/author/joern" title="Posts by Jörn Zaefferer"><img alt='' src='http://www.gravatar.com/avatar/a9d4d2558b560b0ef168ced0f6c5198c?s=36&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D36&amp;r=G' class='avatar avatar-36' height='36' width='36' /><span>2 posts</span>
Jörn Zaefferer</a></li>

<li><a href="http://www.LearningjQuery.com/author/grabanski" title="Posts by Marc Grabanski"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/mgrabanski.jpg" /><span>2 posts</span>
Marc Grabanski</a></li>

<li><a href="http://www.LearningjQuery.com/author/dmethvin" title="Posts by Dave Methvin"><img alt="" src="https://secure.gravatar.com/avatar/161a4cc619398bea1e1714036ed122cf" /><span>1 post</span>
Dave Methvin</a></li>

<li><a href="http://www.LearningjQuery.com/author/danheberden" title="Posts by Dan Heberden"><img alt='' src='http://www.gravatar.com/avatar/31bb633ee786ef231ef5dd164f357600?s=36&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D36&amp;r=G' class='avatar avatar-36' height='36' width='36' /><span>1 post</span>
Dan Heberden</a></li>

<li><a href="http://www.LearningjQuery.com/author/filamentgroup" title="Posts by Filament Group"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/filamentgroup.jpg" /><span>1 post</span>
Filament Group</a></li>

<li><a href="http://www.LearningjQuery.com/author/dougneiner" title="Posts by Doug Neiner"><img alt='' src='http://www.gravatar.com/avatar/6868c2908859c318f4fa0911eb6029b0?s=36&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D36&amp;r=G' class='avatar avatar-36' height='36' width='36' /><span>1 post</span>
Doug Neiner</a></li>

<li><a href="http://www.LearningjQuery.com/author/jonbob" title="Posts by Jonathan Chaffer"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/jchaffer.png" /><span>1 post</span>
Jonathan Chaffer</a></li>

<li><a href="http://www.LearningjQuery.com/author/lrbabe" title="Posts by Louis-Rémi Babé"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/lrbabe.jpg" /><span>1 post</span>
Louis-Rémi Babé</a></li>

<li><a href="http://www.LearningjQuery.com/author/rwhitbeck" title="Posts by Ralph Whitbeck"><img alt='' src='http://www.gravatar.com/avatar/93533aa927a1c348f12903b5ba65aa80?s=36&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D36&amp;r=G' class='avatar avatar-36' height='36' width='36' /><span>1 post</span>
Ralph Whitbeck</a></li>

<li><a href="http://www.LearningjQuery.com/author/jpadolsey" title="Posts by James Padolsey"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/jpadolsey.jpg" /><span>1 post</span>
James Padolsey</a></li>

<li><a href="http://www.LearningjQuery.com/author/clindley" title="Posts by Cody Lindley"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/clindley.png" /><span>1 post</span>
Cody Lindley</a></li>

<li><a href="http://www.LearningjQuery.com/author/dan" title="Posts by Dan Bravender"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/dbravender.jpg" /><span>1 post</span>
Dan Bravender</a></li>

<li><a href="http://www.LearningjQuery.com/author/tane" title="Posts by Tane Piper"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/tpiper.png" /><span>1 post</span>
Tane Piper</a></li>

<li><a href="http://www.LearningjQuery.com/author/brian" title="Posts by Brian Reindel"><img alt="" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/uploads/breindel.jpg" /><span>1 post</span>
Brian Reindel</a></li>
    </ul>
  </div>
  <div id="sidebar-links">
    <h3>Links</h3>
    <ul>
    <li id="linkcat-2" class="linkcat"><h4>jQuery Resources</h4>
  <ul class='xoxo blogroll'>
<li><a href="http://forum.jquery.com" title="Ask questions and get answers &#8212; or help others &#8212; about jQuery, jQuery UI, and plugins">jQuery Forum</a></li>
<li><a href="http://docs.jquery.com/Downloading_jQuery" title="instructions on referencing jQuery from a CDN (at Google, Microsoft, or code.jquery.com) or downloading a copy for local use">Downloading jQuery</a></li>
<li><a href="http://api.jquery.com/" title="Learn more about every method, property, and selector in the jQuery API">jQuery Documentation</a></li>
<li><a href="http://github.com/jquery/jquery/" title="View and download the dev version of jQuery and see a log of changes ">jQuery Code Repository on GitHub</a></li>
<li><a href="http://jquery.org/" title="Learn about the project, the jQuery team, licensing, sponsors, donating, and more">The jQuery Project</a></li>
<li><a href="http://blog.jquery.com" title="The official jQuery blog">jQuery Blog</a></li>
<li><a href="http://blog.jqueryui.com" title="The official jQuery UI blog">jQuery UI Blog</a></li>

  </ul>
</li>
     </ul>
        <a class="firehost" href="http://www.firehost.com?ref=spon_kswedberg_learningjquery"><img src="http://assets.learningjquery.com/images/firehost.jpg" alt="Secure Hosting" /></a>
    <p class="netdna"><a href="http://www.netdna.com" title="LearnngjQuery.com is accelerated by NetDNA.com Content Delivery Network"><img src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/themes/ljq/images/NetDNA_logo_small.png" alt="LearnngjQuery.com is accelerated by NetDNA.com Content Delivery Network" /></a></p>
    <h3>Email Subscription</h3>
    <ul>
      <li>
        <form action="http://feedburner.google.com/fb/a/mailverify" method="post">
          <p>Enter your email address:</p>
          <p><input type="text" name="email"/></p>
          <input type="hidden" value="LearningJquery" name="uri"/>
          <input type="hidden" name="loc" value="en_US" />
          <input type="submit" value="Subscribe" />
        </form>
      </li>
    </ul>

  </div>
</div>

    </div> <!-- end content -->
  </div> <!-- end wrapper -->
  <hr />
  <div id="footer">
    <div class="wrapper">
      <div class="cols4">
        <h2>What Is This?</h2>
        <p>Learning jQuery is a multi-author weblog providing jQuery tutorials, demos, and announcements. We have tutorials for all skill levels, and each entry is categorized by level of difficulty.
        </p>
      </div>
      <!-- <div class="cols4">

       <h2>Support the jQuery Project</h2>
       <p>If you or your organization have benefitted from the use of jQuery, please consider donating to <a href="http://ejohn.org/">John Resig</a> and the <a href="http://jquery.com/">jQuery</a> project. </p>
       <form action="https://www.paypal.com/cgi-bin/webscr" method="post" id="donate">
         <fieldset>
           <input type="hidden" name="cmd" value="_xclick" />
           <input type="hidden" name="business" value="jeresig@gmail.com" />
           <input type="hidden" name="item_name" value="jQuery Donation - Learning jQuery" />
           <input type="hidden" name="item_number" value="JQUERY-DONATE" />
           <input type="hidden" name="no_note" value="1" />
           <input type="hidden" name="currency_code" value="USD" />
           <input type="hidden" name="return" value="http://www.learningjquery.com/" />
           <input type="hidden" name="tax" value="0" />
           <input type="hidden" name="lc" value="US" />
           Amount in $ <input id="amount" type="text" name="amount" value="10.00" size="6" />
           <input type="submit" id="submitdonation" name="submitdonation" value="Donate" />
         </fieldset>
       </form>
     </div>  -->
      <div class="cols2">
        <h2>Pages</h2>
        <ul>
          <li class="page-item page-item-home"><a href="/">Home</a></li>
              <li class="page-item page-item-107"><a href="/categories">Categories</a></li>
    <li class="page-item page-item-106"><a href="/archives">Archives</a></li>
    <li class="page-item page-item-2"><a href="/about">About</a></li>
    <li class="page-item page-item-63"><a href="/contact">Contact</a></li>
        </ul>
      </div>
      <div class="cols2 last">
        <h2>RSS</h2>
        <ul>
          <li><a class="rss" href="feed:http://www.LearningjQuery.com/feed">Entries <span>(RSS)</span></a></li>
          <li><a class="rss" href="feed:http://www.LearningjQuery.com/comments/feed">Comments <span>(RSS)</span></a></li>
        </ul>
              </div>
    </div>
  </div>
  <div id="legal" class="wrapper">
    <a id="creative-commons" href="http://creativecommons.org/licenses/by-sa/2.5/"><img alt="Creative Commons License" border="0" src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/themes/ljq/images/somerights.png" /></a>
    <p>&copy; Copyright 2006&ndash;2013 Learning jQuery and participating authors. Written content on this site is under a Creative Commons License. Code examples are under your choice of MIT or GPL license.</p>
  <p> Development by <a href="http://www.englishrules.com/">Karl Swedberg</a>. Design by <a href="http://www.rexrainey.com/">Rex Rainey</a>. Published with <a href="http://wordpress.org/">WordPress</a>. </p></div>

</div>

Preparation code output


Hi

Hi

Hi

Hi

Hi

Hi

Hi

Hi

Hi

Hi

Hi

Hi

Hi

Hi

Using jQuery’s .pushStack() for reusable DOM traversing methods

read 5 comments

The .pushStack() method has been in jQuery since before version 1.0, but it hasn't received a whole lot of attention outside of core developers and plugin authors. While its usefulness may not be immediately apparent, it can come in really handy in some situations, so I'd like to take a quick look at what it does, how it works, and how we can use it.

pushStack Basics

At its most basic level, the .pushStack() method accepts an array of DOM elements and "pushes" it onto a "stack" so that later calls to methods like .end() and .andSelf() behave correctly. (Side note: As of jQuery 1.4.2, you can pass in a jQuery object instead of an array, but that isn't documented and jQuery itself always uses an array, so that's what we'll stick to here.)

Internally, jQuery uses .pushStack() to keep track of the previous jQuery collections as you chain traversing methods such as .parents() and .filter(). This lets us traverse through the DOM, do some stuff, "back up" to previous collections within the same chain using .end(), and then do something else. Here is a somewhat contrived example:

JavaScript:
  1. // select some divs
  2. $('div.container')
  3.   // find some spans inside those divs and add a class to them
  4.   .find('span').addClass('baby')
  5. // pop those spans off the "stack",
  6. // returning to the previous collection (div.container)
  7. .end()
  8.   // add a class to the parent of each div.container
  9.   .parent().addClass('daddy');

Because .find() returns the result of a .pushStack() call to keep track of the previous collection (as does .parent()), we can use .end() in the above example to return to the container divs.

Using pushStack for Fun and Profit

So, this is great for jQuery, but what can .pushStack() do for me and my code? Well, it can help me write specialized DOM traversal plugins that act just like jQuery's own traversal methods. In other words, I can stop chaining the same sets of traversal methods together and instead write a reusable function that still works with with .end() and all that. For example, let's say I often have a need to find an element's grandparent. While I could write $('#myElement').parent().parent() every time, it might be nice to just be able to write $('#myElement').grandparent() instead. A naïve way to write a grandparent plugin would look like this (changing the method name to "grandpa" for this example):

JavaScript:
  1. // NOT recommended!
  2. (function($) {
  3.   $.fn.grandpa = function() {
  4.     return this.parents().parents();
  5.   };
  6. })(jQuery);

The problem here is that two new jQuery object instances are added to the stack. So, let's see what happens when we use it:

JavaScript:
  1. // The DOM looks like this:
  2. // <div class="grandpa">
  3. //  <div class="pa">
  4. //    <div class="child son"></div>
  5. //  </div>
  6. // </div>
  7.  
  8. var elem = $('div.son').grandpa().end();
  9. $('div.son').text( elem.attr('class') );

Without seeing the plugin, we would expect to see "child son" inserted into <div class="son">, but "pa" is inserted instead. Each .parent() call in the plugin adds to the stack, so using .end() only pops the second one off.

If we use .pushStack() instead, however, we can achieve the expected behavior:

JavaScript:
  1. (function($) {
  2.   $.fn.grandma = function() {
  3.  
  4.     var els = this.parent().parent();
  5.     return this.pushStack( els.get() );
  6.   };
  7. })(jQuery);

Within a plugin function, one that is a method of $.fn, the this keyword refers to the jQuery object; therefore, the els variable refers to a jQuery object, as well. To convert it to an array, we use jQuery's .get() method, and we pass that array to .pushStack(). Let's see if .grandma() works any better than .grandpa().

JavaScript:
  1. // The DOM looks like this:
  2. // <div class="grandma">
  3. //  <div class="ma">
  4. //    <div class="child daughter"></div>
  5. //  </div>
  6. // </div>
  7.  
  8. var elem = $('div.daughter').grandma().end();
  9. $('div.daughter').text( elem.attr('class') );

Here, "child daughter" is inserted, which means that .end() works as expected, changing the jQuery collection from the result of .grandma() to the result of $('div.daughter'). So, we've just successfully written a DOM traversal plugin, albeit a very simple one.

The Simplest DOM Traversal Methods

If the plugin only uses one DOM traversal method, then .pushStack() isn't really necessary. The HTML5 data filter plugin written by Elijah Manor illustrates this point nicely:

JavaScript:
  1. (function($) {
  2.   $.fn.filterByData = function( type, value ) {
  3.     return this.filter(function() {
  4.       return value != null ?
  5.         $(this).data( type ) === value :
  6.         $(this).data( type ) != null;
  7.     });
  8.   };
  9. })(jQuery);

Only one new jQuery collection is added to the stack, via .filter(), so using .end() simply pops that one off, and our job is done.

Filtering grandparents

For the sake of completeness, it would be nice for this DOM traversal plugin to allow optional "filtering" of the parent and grandparent elements. After all, jQuery's .parent() and .parents() allow filtering. For example, if I were to write $('div.child').parent('.daddy'), the jQuery collection would only contain an element if div.child had a parent element and if that parent had a class of "daddy."

There are plenty of reasonable ways one could include the filters, but for my purposes I'm going to have a .grandparent() method optionally accept two arguments. If only one argument is provided, it will filter the grandparent element only; if two are provided, the first will filter the parent and the second will filter the grandparent. Here is the full plugin plugin:

JavaScript:
  1. (function($) {
  2.   $.fn.grandparent = function( parentFilter, grandFilter ) {
  3.     if ( !grandFilter ) {
  4.       grandFilter = parentFilter;
  5.       parentFilter = undefined;
  6.     }
  7.  
  8.     var els = this.parent( parentFilter ).parent( grandFilter );
  9.     return this.pushStack( els.get() );
  10.   };
  11. })(jQuery);

Finally, we have a nice .grandparent() plugin that adheres to the contract set by other jQuery DOM traversal methods—one that works with both filters and the .end() method. Here is what it could look like in use.

comment feed

5 comments

  1. Any comments on .end() vs caching selectors? (eg: parent = $("#parent") )

    Best practice? Performance improvement?

  2. This is an awesome post - thanks for sharing the info on pushStack() as this can be used to help performance when writing code that does heavy dom manipulation.

    @Esteban - without digging too deep, this would be just as performant as "caching" selectors because the essentially that is what pushStack is doing. end() just pops the stack.

    Regarding best practices, I would say, don't get too carried away and have someone have to read through a nested chain with 15 end statements in it and I would say, it's a good practice to use pushStack when writing functions as the jquery team has put this method in place most likely for performance reasons and to ensure that you can easily get back to your parent elements. This allows you to traverse and manipulate the dom based on convention instead of configuration also.

    Just my 2 cents...

  3. @Dan thanks for your reply!
    After seeing this post I created a jsperf test to see if there was any difference in performance:
    http://jsperf.com/jquery-end-vs-cached-selector
    only ran it on Chrome and the seem to perform the same.
    But I agree on the point of not chaining 15 end() calls. I haven't changed any code yet to use end(), but it's really good to know it exists

  4. Awesome, I guess I can improve my current implementation by using this technique. This method is surely under utilized. Thanks for detailed explanation.

  5. Using .pushStack() is so confusing to me, this article helps a lot! Thanks for the tips, I'm gonna start applying a few of them for my new project.

2 Pings

  1. [...] Pushing it It feels like everyday there’s something new to learn about jQuery. This time, how to use jQuery’s pushStack. [...]

  2. [...] Using jQuery’s .pushStack() for reusable DOM traversing methods [...]

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.


Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Find in
$('#box').find('p');
pending…
In context
$('p', '#box');
pending…
Full selector
$('#box p');
pending…
Ch: Find in
$('#box').find('>p');
pending…
Ch: .children()
$('#box').children('p');
pending…
Ch: In context
$('>p', '#box');
pending…
Ch: Full selector
$('#box > p');
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment