htmlcsscss-counter

Why are my <br> tags ignored by CSS counters?


In the code below, it looks like all the <br/> tags are getting ignored by CSS counters. What's the reason for this?

<!DOCTYPE html>
<html>

<head>
  <style>
    body {
      counter-reset: ele;
    }
    *::after {
      counter-increment: ele;
      content: "element count " counter(ele);
    }
  </style>
</head>

<body>

  <h1> h1 </h1>
  <h2> h2 </h2>
  <h2> h2 </h2>
  <h2> h2 </h2>

  <p><b> b </b> p</p>

  <br/>
  <br/>
  <br/>
  <br/>
  <br/>
  <br/>

</body>

</html>

Also, what are elements 7 and 8 representing?


Solution

  • Explanation of the problem(s)

    It looks like all the <br/> tags getting ignored by CSS counters. The reason is?

    <br> tags, like other 'empty' elements, do support counters, but unfortunately, they don't have an ::after pseudo-element. Just like for instance <input> elements, you cannot pull this trick to generate content through CSS. Since incrementing the counter happens in this ::after pseudo-element in your snippet, the counter is not incremented for <br> element because br::after simply doesn't exist.

    In the above snippet, what are elements 7 and 8 representing?"

    The BODY and HTML tags. Since you use ::after, the counter is incremented and the content inserted at the end of those elements, after the other page content.

    Half fix: Count the elements instead of the pseudo-elements

    You can change the CSS slightly to increment the counter on the element itself, and only display the value in the pseudo-element.

    <!DOCTYPE html>
    <html>
    
    <head>
      <style>
        body {
          counter-reset: ele;
        }
        * {
          counter-increment: ele;
        }
        *::after {
          content: "element count " counter(ele);
        }
      </style>
    </head>
    
    <body>
    
      <h1> h1 </h1>
      <h2> h2 </h2>
      <h2> h2 </h2>
      <h2> h2 </h2>
    
      <p><b> b </b> p</p>
    
      <br>
      <br>
      <br>
      <br>
      <br>
      <br>
    
    </body>
    
    </html>

    The example above doesn't quite work yet, because it doesn't increment the counter when going up a level. This is because the counter was already incremented when the element was opened, and closing the HTML and the BODY doesn't increment the counter anymore.

    Workaround: count pseudo-elements and empty elements

    Possible ways to fix it even better: Increment the counter in the ::after after all, but add an extra piece of CSS that increments the counter for elements that don't have an ::after pseudo-element:

    <!DOCTYPE html>
    <html>
    
    <head>
      <style>
        body {
          counter-reset: ele;
        }
        
        /* Increment the counter in the '::after' of non-empty elements. */
        *::after,
        /* And in the element itself for empty element. 
           List taken from https://developer.mozilla.org/en-US/docs/Glossary/Empty_element */
        link,track,param,area,command,col,base,meta,hr,source,img,keygen,br,wbr,colgroup,input
        {
          counter-increment: ele;
        }
        
        *::after {
          content: "element count " counter(ele);
        }
      </style>
    </head>
    
    <body>
    
      <h1> h1 </h1>
      <h2> h2 </h2>
      <h2> h2 </h2>
      <h2> h2 </h2>
    
      <p><b> b </b> p</p>
    
      <br>
      <br>
      <br>
      <br>
      <br>
      <br>
    
    </body>
    
    </html>

    Maybe it's not perfect for every scenario, but your question seems to be more academical anyway. Anyway, I hope this explanation at least helps you understand why in your snippet those <br> elements don't seem to have a counter at all.