Detecting "runs" of DOM elements in jQuery

What is the best way to detect runs of dom elements in jQuery?

For example, if I have the following list of items

<ol>
  <li class="a"></li>
  <li class="foo"></li>
  <li class="a"></li>
  <li class="a"></li>
  <li class="foo"></li>
  <li class="foo"></li>
  <li class="foo"></li>
  <li class="a"></li>
  <li class="foo"></li>
  <li class="foo"></li>
  <li class="foo"></li>
</ol>

Let's say I want to grab all the elements li.fooand wrap them inside my own block <ol>(or any other wrapper) to make something like this.

<ol>
  <li class="a"></li>
  <li class="foo"></li>
  <li class="a"></li>
  <li class="a"></li>
  <li><ol>
    <li class="foo"></li>
    <li class="foo"></li>
    <li class="foo"></li>
  </ol></li>
  <li class="a"></li>
  <li><ol>
    <li class="foo"></li>
    <li class="foo"></li>
    <li class="foo"></li>
  </ol></li>
</ol>

As you can see from the example, I only want to wrap the "runs" of the li.foodom elements (where there are two or more elements li.foo).

I'm not sure about the best / most efficient way to accomplish this with jQuery (or just using javascript).

+5
source share
4 answers

: http://jsfiddle.net/kNfxs/1/

$('ol .foo').each(function() {
    var $th = $(this);
    var nextUn = $th.nextUntil(':not(.foo)');
    if(!$th.prev('.foo').length && nextUn.length)
        nextUn.andSelf().wrapAll('<li><ol></ol></li>');
});

.foo, .foo, 1 .foo, .foo, nextUntil() (docs) , andSelf() (docs), , wrapAll() (docs).


: , nextUntil() (docs), .foo().

: http://jsfiddle.net/kNfxs/2/

$('ol .foo').each(function() {
    var $th = $(this);
    if($th.prev('.foo').length) return; // return if we're not first in the group
    var nextUn = $th.nextUntil(':not(.foo)');
    if( nextUn.length)
        nextUn.andSelf().wrapAll('<li><ol></ol></li>');
});
+3

- :

$(li).each(function(){
if($(this).next().hasClass('foo')){
---- Recursively check until the end of the run has been found ----
}
});

, , .

0

, :

var $foo = $('.foo + .foo');
$foo.add($foo.prev());

$ foo will be a set of " .fooworks"

Edit to add:

I thought of a simpler way:

var $foo = $('.foo + .foo').prev('.foo').andSelf();
0
source

Working example: http://jsfiddle.net/v8GY8/

For posterity:

// Takes a jQuery collection and returns an array of arrays of contiguous elems
function groupContiguousElements( $elems ){
  var groups = [];
  var current, lastElement;
  $elems.each(function(){
    if ($(this).prev()[0] == lastElement ){
      if (!current) groups.push( current=[lastElement] );
      current.push( this );
    }else{
      current = null;
    }
    lastElement = this;
  });
  return groups;
}

var groups = groupContiguousElements( $('li.foo') );
$.each( groups, function(){
  var wrapper = $('<ol>').insertBefore(this[0]);
  $.each(this,function(){
    wrapper.append(this);
  });
});
0
source

All Articles