开发者

Detecting "runs" of DOM elements in jQuery

开发者 https://www.devze.com 2023-02-09 13:03 出处:网络
What is the best way to detect runs of dom elements in jQuery? For instance, if I have the following list of items

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

For instance, 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>

Say I want to grab all the li.foo elements and wrap them inside their own <ol> block (or any wrapper for that matter) to end up with something like.

<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 "runs" of li.foo dom elements (where there are 2 or more li.foo elements in succession.

I'm not sure of the best/most efficient way to accomplish this via jQuery (or just plain javascript for that matter).


Example: 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>');
});

Loop over the .foo elements, and if the previous element is not .foo, and it has at least 1 .foo after it, then grab all the next .foo elements using the nextUntil()(docs) method and include the original using the andSelf()(docs) method, then wrap them using the wrapAll()(docs) method.


Update: This will be a little more efficient because it avoids the nextUntil()(docs) method when there's a previous .foo().

Example: 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>');
});


Use something like this:

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

To recursively check write a function that checks the next element until the end of the run has been found.


Not sure how well this works for performance:

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

$foo will be the set of ".foo runs"

Edit to add:

I thought of a simpler way:

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


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

精彩评论

暂无评论...
验证码 换一张
取 消