12

How can I specify :first-of-type of the entire document?

I want to style the first <p> of the HTML, no mater where it is located (I don't want to write section p:first-of-type because it may be located elsewhere in a different HTML document).

p {
  background:red; 
}

p:first-of-type {
  background:pink;
}

p:last-of-type {
  background:yellow; 
}
<body>
  <section>
    <p>111</p>
    <p>222</p>
    <p>333</p>
  </section>
  <p>444</p>
  <p>555</p>
</body>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
user3289092
  • 129
  • 1
  • 3

1 Answers1

7

With CSS alone this unfortunately isn't possible. The documentation for the :first-of-type pseudo-class states:

The :first-of-type pseudo-class represents an element that is the first sibling of its type in the list of children of its parent element.

This means that :first-of-type is applied to the first element of its type relative to its parent and not the document's root (or the body element, in this case).


JavaScript solutions

:first-of-type

We can achieve this by introducing some JavaScript. All we need for this is JavaScript's querySelector() method, which pulls the first matching element from the selector specified.

In this example I've altered your :first-of-type pseudo-class to instead be a class of "first-of-type", then used JavaScript to add this class to the element returned when using querySelector('p'):

document.querySelector('p').className += ' first-of-type';
p {
  background:red; 
}


p.first-of-type {
  background: pink;
}
<body>
  <section>
    <p>111</p>
    <p>222</p>
    <p>333</p>
  </section>
  <p>444</p>
  <p>555</p>
</body>

:nth-child and :last-of-type

As for :nth-child and :last-of-type, we can instead make use of a similar method JavaScript gives us: querySelectorAll(). This method pulls all matching elements into a NodeList (which is similar to an array), which we can then iterate through or select specific elements from within through the index:

var elems = document.querySelectorAll('p');

// nth-of-type = NodeList[n - 1]
// e.g. to select the 3rd p element ("333"):
if (elems.length >= 2)
   elems[2].className += ' nth-of-type';

// last-of-type = NodeList length - 1
if (elems.length)
   elems[elems.length - 1].className += ' last-of-type';
p {
  background:red; 
}


p.nth-of-type {
  background: pink;
}

p.last-of-type {
  background: yellow;
}
<body>
  <section>
    <p>111</p>
    <p>222</p>
    <p>333</p>
  </section>
  <p>444</p>
  <p>555</p>
</body>

Note that I've included if statements around both selectors to ensure the elems NodeList has enough elements, otherwise an error will be thrown.

James Donnelly
  • 126,410
  • 34
  • 208
  • 218
  • For the fastest solution, i would prefer js too, dont waste time searching, use js instead – Mephiztopheles Dec 17 '14 at 11:48
  • To get the nth matching element simply index off querySelectorAll: `var elements = document.querySelectorAll('p'); elements[elements.length - 1].className += ' last';` – BoltClock Dec 17 '14 at 15:29
  • @BoltClock seems I'd completely neglected to respond to that part of the question! I'll update my answer. – James Donnelly Dec 17 '14 at 15:37
  • Is there any chance that this feature will be added on the CSS4 spec? – Vandervals Jul 14 '15 at 09:13
  • @Vandervals based on the current state of the [Selectors Level 4](http://www.w3.org/TR/selectors4/) Working Draft it doesn't look like something which will be added unfortunately. – James Donnelly Jul 14 '15 at 09:16
  • I am considering marking [this old question](http://stackoverflow.com/questions/2751127/select-first-second-or-third-element-with-given-class-name) I answered several years before as a duplicate of the one you've answered here. All but two of the answers there (one of which is not my own) misinterpreted the question (one going so far as to take the code sample completely literally), and frankly the question isn't nearly as well-written or clear as the one here, though in fairness this one was edited heavily by you and Hashem. – BoltClock Feb 25 '16 at 03:28