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);
});
});
精彩评论