First and last matching child in CSS and with jQuery

I recently noticed a few problems in some code I had written a few months ago:

  1. I am displaying a grid containing an empty first column and an empty last column and want to remove them. This only happened for the first row and not for the others
  2. I display a left border on all columns and additionally a right border on the last column. This also didn’t work anymore, the right border was missing.

These two problems were due to using the selector (once in CSS and once with jQuery) which do not do what I would expect.

First let’s assume we have a table-like div structure:

<div class="table">
    <div class="tr">
        <div class="th"></div>
        ...
        <div class="th"></div>
    </div>
    <div class="tr">
        <div class="td"></div>
        ...
        <div class="td"></div>
    </div>
    ...
    <div class="tr">
        <div class="td"></div>
        ...
        <div class="td"></div>
    </div>
</div>

In CSS, I can set the style of the first cell and of the last cell of each row like this:

div.tr div:first-child { }

div.tr div:last-child { }

To select them with jQuery:

jQuery("div.tr div:first-child")

jQuery("div.tr div:last-child")

Easy, right ? So where’s the problem ?

Well the problem is that is that it only worked by chance and as soon as I extended the generated html code, it broke. I needed to add some additional divs at the end of every line in the grid but have them be invisible. So basically, since the new divs where invisbile, the current last div would still have to be styled in a special way although it wasn’t the last div child anymore. So I just modified my CSS and jQuery selectors like this:

div.tr div.td:last-child { }
jQuery("div.tr div.d:last-child")

And the same for th as well. But it doesn’t work. What happens is that div.tr div.td:last-child doesn’t mean select the last child of div.tr being a div.td. It means select the last child of div.tr if it is a div.td. So since the last child of div.tr was not a div.td anymore, it failed.

So what I needed was a selector like :last-child-of-type. Well actually there is a selector :last-of-type. Unfortunately, it also failed using it since this selector doesn’t work with classes i.e. you can use div:last-of-type as selector but not div.class:last-of-type. So :last-of-type is useful to select the second div in this case:

<article>
    <p></p>
    <div></div>
    <div></div>
    <div></div>
    <p></p>
</article>

But not in our case. So what can be done ? Well, the way I went for (which just involved replacing an append by a prepend) was to move the hidden divs to the beginning of the row. So now I could use :last-child again. And since I was selecting the first line only with jQuery, I used the following:

$(".tr.cell").each(function() {
    $(this).children(".td").first().remove();
    $(this).children(".td").last().remove();
});

So it’d be great if :last-of-type could support classes or if there was a :last-of-class. But until there is, you can work around it with jQuery using a loop. In CSS, unfortunately, there seems to be no way to do it. The only way to select this last element of a type using CSS selectors is to assign it a class with javascript or while generating the HTML code and then selecting this class using CSS.

Leave a Reply

Your email address will not be published.