Question

jQuery currently has .next(filter) and .nextAll(filter) but I need something that fits in the middle of these - effectively, a .nextWhile(filter) that repeatedly does next until the filter is no longer true, then stops (rather than continuing to the end).

To demonstrate this, the following is some simplified HTML - (in reality, it is dynamically generated, random order/data, more columns, proper class names, and so on).

<table>
    <thead>
        <tr>
            <th>...</th>
        </tr>
    </thead>
    <tbody>

        <tr class="x"><td>a <button>Show/Hide</button></td></tr>
            <tr class="x y"><td>a1</td></tr>
            <tr class="x y"><td>a2</td></tr>

        <tr class="z"><td>b</td></tr>

        <tr class="z"><td>c</td></tr>

        <tr class="x"><td>d <button>Show/Hide</button></td></tr>
            <tr class="x y"><td>d1</td></tr>
            <tr class="x y"><td>d2</td></tr>
            <tr class="x y"><td>d3</td></tr>

        <tr class="z"><td>e</td></tr>

        <tr class="x"><td>f</td></tr>

        <tr class="x"><td>g <button>Show/Hide</button></td></tr>
            <tr class="x y"><td>g1</td></tr>
            <tr class="x y"><td>g2</td></tr>

    </tbody>
</table>

And against this some JavaScript is run:

<script type="text/javascript">
    var $j = jQuery.noConflict();

    $j().ready(init);

    function init()
    {
        $j('tr.y').hide();
        $j('tr.x button').click( toggleRelated );
    }

    function toggleRelated()
    {
        // Only toggles one row
        // $j(this).parents('tr').next('.y').toggle();

        // Toggles unrelated ones also
        $j(this).parents('tr').nextAll('.y').toggle();

        // Not currently a jQuery construct
        // $j(this).parents('tr').nextWhile('.y').toggle();
    }

</script>

Is there an easy way to implement this nextWhile construct?

Ideally this needs to be achieved without modifying the current HTML.

Was it helpful?

Solution

In jQuery you can create nextWhile() equivalent using nextUntil() method and :not selector. Simply put, while (condition is true) { do something } is same as saying until (condition is false) { do something }.

Consider this trick example where you are required to select all .child elements that follow the first .parent:

<ul id="test">
  <li class="parent"></li>
  <li class="child"></li>
  <li class="child"></li>
  <li class="child"></li>
  <li class="divider"></li>
  <li class="parent"></li>
  <li class="child"></li>
  <li class="child"></li>
  <li class="child"></li>
</ul>

The following code is roughly equivalent to .nextWhile(".child") and selects the three .child elements:

$("#test .parent:first").nextUntil(":not(.child)");

OTHER TIPS

UPDATE: Gave up on the recursion idea and decided to count and slice (see last example).

Initial Attempt:
This code is buggy - it only returns the last two items, so doesn't work for 3+ items.

jQuery.fn.nextWhile = function(f)
{
    if( this.next(f).html() )
    {
        return this.next(f).nextWhile(f).andSelf();
    }
    else
    {
        return this;
    }
};


Current Version:

jQuery.fn.nextWhile = function(f)
{
    var Next = this.next(f);
    var Pos = 0;

    while( Next.length > 0 )
    {
        Pos++;
        Next = Next.next(f);
    }

    return this.nextAll(f).slice(0,Pos);
}

This appears to work fine, but I'm not sure if there are any performance penalties to selecting everything and then slicing only a handful from it?

My version:

$.fn.extend({
  nextWhile: function(selector) {
    var $result = $([]);
    this.each(function() {
      var $next = $(this).next();
      while ($next.is(selector)) {
        $result = $result.add($next);
        $next = $next.next();
      }
    });
    return $result;
  }
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top