19

I have an awkward problem, due to inheriting a shedload of really, really badly formatted HTML.

Basically I have some table rows like this:

<tr class="odd">...</tr>
<tr class="odd">...</tr>
<tr class="odd">...</tr>
<tr class="even">...</tr>
<tr class="even">...</tr>
<tr class="even">...</tr>
<tr class="odd">...</tr>
<tr class="odd">...</tr>
<tr class="odd">...</tr>

And what I need is a jQuery selector that will give me the first element of each block of classes. Is that clear enough? I'm not sure how else to explain it.

So in the example case I would want rows 1, 4, and 7.

Ive done this so far by selecting every nth child, but Ive now realised this wont work as there wont always be the same number of rows in each block of classes.

Any help you guys can give it appreciated, as always :)

Cheers!

martynas
  • 12,120
  • 3
  • 55
  • 60
Tyler Durden
  • 317
  • 3
  • 4
  • 11
  • For the first two, you can use `querySelector` which returns the first Node in a NodeList. For the next block, I'm not 100% sure lemme think about it for a minute. – Sterling Archer May 15 '14 at 14:13
  • is it possible that for example the first **odd** block exists out of 3 elements, the first **even** out of 2 and the last **odd¨** out of 4? – Mivaweb May 15 '14 at 14:15
  • @VDesign for an algorithm like this, you don't want to think static patterns. – Sterling Archer May 15 '14 at 14:15
  • @RUJordan I am just wondering if **odd** blocks consist out of the same amount of childs or not – Mivaweb May 15 '14 at 14:17

5 Answers5

28

You'll need to employ some strategic use of :not() and adjacent sibling selectors:

$('tr.odd:first-child, tr.even:first-child, tr:not(.odd) + tr.odd, tr:not(.even) + tr.even')

The tr:not(...) + tr... bits select any tr element that's directly preceded by a tr that does not have the same class name. You can't do this dynamically (e.g. with some sort of wildcard), but it's entirely possible if you specify each class name separately. This will ensure you only select the first element in each contiguous group.

The reason the :first-child pseudo is required is simply because + won't match the first child, since it implies a relationship between an element and its preceding sibling. Note that :first-child accounts for the very first tr only. You could replace tr.odd:first-child, tr.even:first-child with simply tr:first-child if every tr element will have exactly one of either class name, but I specify them anyway for clarity's sake.

Last but not least, this doubles as a valid CSS selector, so you can use it in a stylesheet as well if you like.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • Spot on! Really nice solution. – emerson.marini May 15 '14 at 14:18
  • 2
    it's a pity that I've just found this solution but you had posted this 1 minute before. – King King May 15 '14 at 14:19
  • @boltclock what the last two selector will select "tr:not(.odd) + tr.odd, tr:not(.even) + tr.even'" ? – SivaRajini May 15 '14 at 14:21
  • @SivaRajini a sequence of a not-odd and an odd + the opposite. Really smart one, gj BoltClock – alou May 15 '14 at 14:24
  • @SivaRajini: I updated my answer with an explanation. – BoltClock May 15 '14 at 14:24
  • @BoltClock thanks.please clear my doubt "tr:not(.odd) + tr.odd" how this selector will select another odd cluster first element ? – SivaRajini May 15 '14 at 14:32
  • why didn't you simply use `tr.even + tr.odd` instead of `tr:not(.odd) + tr.odd` ..? both are same right..? – Rajaprabhu Aravindasamy May 15 '14 at 14:39
  • @Rajaprabhu Aravindasamy: I could, but I wanted to highlight the fact that using `:not()` simply means the element must come after an element that does not have the same class. If the question had more than two class names to work with, it'd be far better to use `:not()`. Listing the different class names would require you to repeat it once for every other class name. – BoltClock May 15 '14 at 14:41
10
$('tr.odd:not(.odd+.odd),tr.even:not(.even+.even)')

This just takes any .odd element which isn't following another .odd, and any .even element which isn't following another .even element.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 3
    This answer is also correct. It shouldn't be downvoted. – emerson.marini May 15 '14 at 14:22
  • I've never used `:not()` in this manner, it's absolutely a new usage to me. – King King May 15 '14 at 14:31
  • 2
    @King King: It's a non-standard syntax invented by jQuery. The question *is* about using jQuery to select elements though, so the answer isn't wrong *per se*. But a purist might prefer a valid CSS selector for various reasons if it is in fact possible with a CSS selector. See also http://stackoverflow.com/questions/10711730/whats-the-difference-in-the-not-selector-between-jquery-and-css – BoltClock May 15 '14 at 14:34
  • @BoltClock thanks for the info, maybe that's why it's so new to me. – King King May 15 '14 at 14:36
  • 1
    In CSS, `:not` only takes a *simple selector* -- a single class name, id, pseudo-class, or possibly an attribute selector (i forget). If you're using jQuery-specific stuff, it'd be better to split that off. Maybe something like `$('tr.even, tr.odd').not('.even + .even, .odd + .odd')`, which would avoid the odd selector syntax altogether. – cHao May 15 '14 at 21:11
1

this may help you

$('button').click(function() {
    var lastGroup = ''
    $('.groups tr').each(function() {
        if(lastGroup != this.className) {
            $(this).css('background-color', 'red');
            lastGroup = this.className;
        }
    });
});

explanation: loop through all the TR and verify if the class name (this.className) is equal to lastGroup. if not equal, we have a new group (first element of the group). if equal, ignore and continue. this will work with any class name used.

JSFiddle: http://jsfiddle.net/bq7zB/

arvic.rivera
  • 673
  • 5
  • 6
-2

Loop through and check if the current element matches the last one, otherwise it's the first of its set:

var lastClass = false;
$('tr.odd, tr.even').each({ function() {
    lastClass = $(this).attr('class');
    if($(this).attr('class') != lastClass){
        // Do your conditional stuff here
    }
}
Reece
  • 396
  • 3
  • 11
-5
  var prevClass = '';
  $('tr').each(function(){
    var thisClass = $(this).attr('class');
    if (prevClass === thisClass) {
     //do nothing?   
    } else {
    //push in array? do something to the row?   
    }
 });
alou
  • 1,432
  • 13
  • 16