CSS-Plus
CSS-Plus
Play with web technologies

Prevent :last-child from slowing you down

May 29, 2011
Attention: This article is old and the content is outdated. I've left the article here for historical purposes.
Prevent :last-child from slowing you down

:first-child is a CSS selector that has been around since the CSS 2.1 spec. It is extremely useful and can be used effectively in various ways.

:last-child has finally made an appearance in CSS 3. One would think that selectors such as first and last child are pretty much the same and would be implemented at the same time. This is not the case and they have pretty big performance differences.

Why isn't :last-child included within CSS2?

Jonathan Snook has an extremely informative article called Why we don't have a parent selector. It is an interesting & in depth article that covers why :last-child is is so difficult for the browser to render and the article also has examples of :first-child vs :last-child.

Simply put, :last-child only functions correctly once the entire page has loaded. The way :last-child is rendered while the page is busy loading is executed differently throughout the different browsers.

Never use :last-child

I feel that last-child is not necessary in most cases. I say most cases to cover myself but I haven't run into a situation where it's a necessity. The only times I find I need first/last-child is when I'm styling some sort of list.

A typical example would be: padding-right and/or border-right applied to a navigation's list items.

#nav li{border-right: 1px solid black; padding-right: 20px;}
The padding and border isn't ideal on the last <li> element. This is what we want: It's pretty simple to solve with :last-child
#nav li:last-child{border-right: 0; padding-right: 0;}
This, however, won't even work on LTE IE8.

Use :first-child instead

:first-child is contained within the CSS2.1 spec and therefore is supported within IE8 and has buggy support within IE7. It would be a FAR better idea to make use of this.

Swap the properties around — Instead of border/padding right, set it to border/padding left.

#nav li{border-left: 1px solid black; padding-left: 20px;}

The menu example would look something like this: You can solve this problem very easily without :last-child and have it supported by IE7(buggy) and 8.

#nav li:first-child{border-left: 0; padding-left: 0;}

It's very easy to convert your CSS into very efficient and cross browser friendly CSS — All you need to do is think about what it is you are trying to doing and imagine how else it could be achieved.

I wish it worked properly in IE7

What is the deal with IE7 and :first-child? Very basically, IE7 will see a comment as the first child. In IE7 li:first-child would not work on the following html:
<ul>
	<!-- This is a comment -->
	<li>One</li>
	<li>One</li>
	<li>One</li>
</ul>
Now that we've established that things can be done more efficiently and in a cross-browser friendly fashion without too much difficulty, would it be possible to make :first-child function properly within IE7 even if there are html comments throughout the html?

Your first thought might be:

No, it's the way IE7 renders HTML. Let's move onYour first thought
What selectors do IE7 support that are more reliable and could solve this simple problem? IE7 supports adjacent selectors. I really love adjacent selectors and I think it is very under-rated. Chris Coyier has an article which explains adjacent selectors and other selectors too.

Basically, an adjacent selector selects the element that directly follows the targeted element. For example:

p + p{background: red}

This would select any paragraph element that has a preceding paragraph element. This would mean that the first paragraph element would not be selected because it does not contain any preceding paragraph element.

Pseudo-select via Non-selection

By this I mean that you could add a left border to every list item except the first. This could create the illusion of :first-child.
li + li{border-left: 1px solid black; padding-left: 20px}
This probably isn't as efficient as :first-child, but it does add to the cross-browser compatibility and it can be of great use in many cases.