views:

974

answers:

4

I've got a list of elements and I want to use the header divs to separate them after the pages loaded up. So the code below,

<div class="header">Header 1</div>
<div class='test'>Test 1</div>
<div class='test'>Test 2</div>
<div class='test'>Test 3</div>
<div class="header">Header 2</div>
<div class='test'>Test 4</div>
<div class='test'>Test 5</div>
<div class='test'>Test 6</div>
<div class='test'>Test 7</div>
<div class='test'>Test 8</div>
<div class="header">Header 3</div>
<div class='test'>Test 9</div>
<div class='test'>Test 10</div>
<div class='test'>Test 11</div>
<div class='test'>Test 12</div>
<div class="header">Header 4</div>
<div class='test'>Test 13</div>
<div class='test'>Test 14</div>

Would become,

<div class='wrap'>
<div class="header">Header 1</div>
<div class='test'>Test 1</div>
<div class='test'>Test 2</div>
<div class='test'>Test 3</div>
</div>
<div class='wrap'>
<div class="header">Header 2</div>
<div class='test'>Test 4</div>
<div class='test'>Test 5</div>
<div class='test'>Test 6</div>
<div class='test'>Test 7</div>
<div class='test'>Test 8</div>
</div>
<div class='wrap'>
<div class="header">Header 3</div>
<div class='test'>Test 9</div>
<div class='test'>Test 10</div>
<div class='test'>Test 11</div>
<div class='test'>Test 12</div>
</div>
<div class='wrap'>
<div class="header">Header 4</div>
<div class='test'>Test 13</div>
<div class='test'>Test 14</div>
</div>

Any ideas? Thanks in advance.

+3  A: 

What you are asking to do is a terrible idea. That's the kind of stuff you should do server-side. (There are always exceptions).

That being said, the following code should do what you ask.

$('.header').each(function() {
  var head = $(this);

  if(!head.parent().hasClass('wrap')) {
    head.before("<div class=\"wrap\"></div>");

    var wrap = head.prev();
    var curr = head;

    do {
      var currEl = curr;
      curr = curr.next();

      currEl.appendTo(wrap);
    } while(curr.length > 0 && !curr.hasClass('header'));
  }
});

NOTE: I do not usually develop in jQuery, so sorry if I don't follow whatever the standard way of doing jQuery is.

Andrew Moore
Agreed! Not a very good idea, there's no reason to use jQuery here.
Doomspork
even if you go trough with this, i'd suggest changing it to not header instead of while .hasClass('test')...the elements between the headers don't necessary all have the same class (they do in the example, but who knows what the real code says)
Sander
oh and if the headers are actually created serverside, then i would indeed do the wrapping div also on the server side.but you could have a page with a gallery of employees and when you click on sort of a filter button, to group them by office location you could dynamically add all divs with persons from office 1 into a wrapper (with the header div added) and people from office 2 into a wrapper etc...but the way the question is asked, makes me think the header is printed server side so i would find it odd not to do the wrapper server side too.
Sander
If you are going to -1 me, at least have the decency of telling why. The above works perfectly.
Andrew Moore
It's not uncommon to do this kind of HTML transformation completely client-side in something like, say, a rich HTML edit box.
Joe Chung
In such context it is fine... I still don't see why the -1 when the code works.
Andrew Moore
**@Joe Chung:** If you read the original question, the manipulation is done onLoad... Which is why I said it was a bad idea. From a non-obstructive point of view, you are better of doing it server-side.
Andrew Moore
"after the pages loaded up" is not the same as "on load."
Joe Chung
Excellent thanks, works perfectly.
Perhaps this isn't always a good idea, but could form the basis of dynamically creating tab groups for tabbed interaction. In this particular (edge?) case, I would not consider it a terrible idea
Andy Ford
**@Andy Ford:** There are always edge cases to every situation. But for close to 95% of situations you are going to encounter, you don't want to use this method.
Andrew Moore
+2  A: 

Here is another method. Not quite as pretty as Andrew's (EDIT: although I tried his and it's not working? I'm sure it's just some minor oversight) but does essentially the same thing:

jQuery(function($){ 

  var $everything = $('.header,.test'); 
  var splitAtHeaders = []; 

  $everything.each(function(index){ 
    var $item = $(this); 
    if ('header'===$item.attr('className') || !splitAtHeaders.length) { 
      splitAtHeaders[splitAtHeaders.length] = []; 
    } 
    splitAtHeaders[splitAtHeaders.length-1].push($item); 
  }); 

  $.each(splitAtHeaders, function(){ 
    var currentWrapper = null; 
    $.each(this, function(index){ 
      if (0===index || !currentWrapper) { 
        currentWrapper = this.wrap('<div class="wrap"></div>'); 
      } 
      else { 
        currentWrapper.append(this); 
      } 
    }); 
  }); 

});

Here is a demo: http://jsbin.com/ojoqi/edit

But I do agree that this is something that should be handled server-side if you can help it.

EDIT: I tried fixing Andrew's solution. Here's what I came up with:

$('.header').each(function() {
  var next = $(this).next();
  var head = $(this).wrap('<div class="wrap"></div>');
  while (next && next.hasClass('test')) {
    var curr = next;
    next = next.next();
    head.append(curr);
  }    
});
brownstone
that fix turned out pretty, I like it.
epalla
A: 

Assumption: Headers will have different text. If not, use something else to distinguish the headers, like an id attribute.

$("div.header").each(function() {
    var $header = $(this);

    var $tests = $.grep(
        $("div.test"),
        function(n) {
            return $(n).prevAll("div.header").text() == $header.text();
        });

    $.merge($header, $tests).wrapAll($("<div class='wrap'>"));
});
Joe Chung
A: 

I'd do it like this:

var divs = $( 'div' );
var headers = divs.filter( '.header' );

if ( headers.length != 0 ) {
  var start = 0;
  var wrapper = $( '<div class="wrap" />' );

  headers.each( function() {
    var pos = divs.index( this );
    divs.slice( start, pos - 1 ).wrapAll( wrapper.clone() );
    start = pos;
  } );

  divs.slice( start ).wrapAll( wrapper.clone() );
}
Juan