tags:

views:

1279

answers:

4

I have a page that returns a dynamic list of items

<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
<li>item6</li>
</ul>

I need a way to insert tags around every grouping of 3 list elements. Basically transform the code above into this

<ul>
 <div>
  <li>item1</li>
  <li>item2</li>
  <li>item3</li>
 </div>
 <div> 
  <li>item4</li>
  <li>item5</li>
  <li>item6</li>
 </div>
</ul>

I have been playing around with the Jquery Wrap(),WrapAll(), WrapInner()... but with no success so far. Greatly appreciate any solutions or ideas on this.

+1  A: 

This is solution is for the suggested in comments to insert new ul instead of div.

jQuery:

var numbGroups = ($('ul li').length - 3)/3;
for(var index = 0; index < numbGroups ; index++) {
$('#ul'+index).after("<ul id='ul"+(index+1)+"'></ul>");
$('#ul' + (index+1)).append($('ul#ul0 li:gt(2):lt(3)'));
}

Inserting a Id in the ul

<ul id='ul0'>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
<li>item6</li>
<li>item7</li>
<li>item8</li>
</ul>

EDIT: The old solution was wrong for longer lists. Corrected that.

Daniel Moura
A: 

How about this:

var originalList = $("ul");
var listsToAdd = originalList.children("li").length / 3 - 1;
for(var index = 0; index < listsToAdd; index++)
{
 originalList.before("<ul></ul>");
}
while (originalList.children("li").length > 3)
{
 originalList.children("li:lt(3)").appendTo("ul:empty:first");
}

It worked in my tests with larger lists and no id needs to be added (assuming there is only one list to start. Otherwise, just set originalList to the list to be divided.)

Ben Koehler
+2  A: 

Here's another method that generates the HTML dynamically and follows correct semantics allowing only <li> elements directly inside of <ul> elements:

$(function(){
    const GROUP_COUNT = 3;
    var liElements = $('ul > li');
    var count = liElements.length;
    var html = '';

    for (var i = 0; i < count; i += GROUP_COUNT) {
     html += '<li><ul>';
     liElements.slice(i, i + GROUP_COUNT).each(function(){
      html += '<li>' + $(this).html() + '</li>';
     });
     html += '</ul></li>';
    }

    $('ul').html(html);
});

You can change GROUP_COUNT to group by a different number of elements.

This code will transform:

<ul>
  <li>item1</li>
  <li>item2</li>
  <li>item3</li>
  <li>item4</li>
  <li>item5</li>
  <li>item6</li>
  <li>item7</li>
</ul>

Into:

<ul>
  <li>
    <ul>
      <li>item1</li>
      <li>item2</li>
      <li>item3</li>
    </ul>
  </li>
  <li>
    <ul>
      <li>item4</li>
      <li>item5</li>
      <li>item6</li>
    </ul>
  </li>
  <li>
    <ul>
      <li>item7</li>
    </ul>
  </li>
</ul>
John Rasch
Thanks everyone. This seem to be the most valid and at same time accomplished what I needed.
Dan
A: 

I would, like suggested before, advice not to include some div elements inside ul. Anyhow, here is my proposal for this:

for(var i = 2; i < $('ul li').length - 1; i += 3)
{    $('ul li').eq(i).after('</div><div>');    }
$('ul').wrapInner('<div></div>');
Benoit Vidis