103

I'm having trouble understanding the behavior of the CSS :after property. According to the spec (here and here):

As their names indicate, the :before and :after pseudo-elements specify the location of content before and after an element's document tree content.

This doesn't seem to place restrictions on which elements can have a :after (or :before) property. However, it seems to only work with specific elements... <p> works, <img> doesn't, <input> doesn't, <table> does. I'm could test more, but the point is made. Note that this seems pretty consistent across browsers. What determines whether an object can accept a :before and :after property?

eykanal
  • 23,724
  • 17
  • 75
  • 107

4 Answers4

159

img and input are both replaced elements.

A replaced element is any element whose appearance and dimensions are defined by an external resource. Examples include images (<img> tags), plugins (<object> tags), and form elements (<button>, <textarea>, <input>, and <select> tags). All other elements types can be referred to as non-replaced elements.

:before and :after only work with non-replaced elements.

From the spec:

Note. This specification does not fully define the interaction of :before and :after with replaced elements (such as IMG in HTML). This will be defined in more detail in a future specification.

With span:before, span:after, the DOM looks like this:

<span><before></before>Content of span<after></after></span>

Evidently, that won't work with <img src="" />.

julienc
  • 15,239
  • 15
  • 74
  • 77
thirtydot
  • 210,355
  • 44
  • 377
  • 337
  • 2
    "Generated content does not alter the document tree." So I would be surprised if you were able to find something like "``" in the source. – Knu Aug 04 '11 at 22:36
  • 4
    @Knu: You're right, I explained it poorly. You will never find ``. I should've said "pretend DOM" or something. The point was to show that `:before` and `:after` are inside the element, not outside it. – thirtydot Aug 04 '11 at 22:39
  • No, they are not stored in the shadow DOM. – tool Jun 24 '14 at 12:19
  • 3
    The spec quoted does not say that `:before` and `:after` do not work for replaced elements; it just says that it does not “fully define” how and that this will be define in future (which has not happened so far). What happens in DOM is irrelevant here. While browsers do not support support these pseudo-elements for some elements, this is *not* in the specs, just what implementations do. – Jukka K. Korpela Oct 15 '14 at 05:55
  • I used to consider logical the explanation that `::before` and `::after` don't work for _void_ (empty) element that never have actual content, before/after which they could apply. But surprisingly, they work for `hr` element in almost all browsers. – Ilya Streltsyn Dec 11 '14 at 08:08
  • WebKit based browsers do tend do let you use pseudo-elements with replaced elements, but it still doesn't usually work in other browsers. – thirtydot Jun 02 '16 at 07:24
  • The funny thing is, it's mentioned on a [W3C note](https://www.w3.org/TR/CSS-access#Alt) (Accessibility Features of CSS, 4 August 1999). And according to [this](https://makandracards.com/makandra/8417-you-cannot-use-before-or-after-on-img-in-css), it is supported on older version of Opera, but later got dropped. – widyakumara Mar 21 '18 at 22:59
9

:before and :after are not required to work for replaced elements, and CSS specifications do not specify how they would work for them, and the concept of replaced element is somewhat vague.

The CSS 2.1 specification clearly suggests that they can work for replaced elements, just saying that it does not “fully define” how. This relates to the issue that a replaced element is expected to have its own visual rendering, which is not controlled by CSS, whereas the pseudo-elements should add something to the content of the element. The spec adds that this will be defined “in more detail” in a future specification, but this has not taken place so far.

Browser vendors just decided to avoid problems by not implementing these pseudo-elements for some elements at all.

It is not clear at all what “replaced element” means, and the meaning appears to have changed somewhat. It is often interpreted as meaning the same as empty element (an element with EMPTY declared content, i.e. an element that cannot have any content), but CSS 2.1 itself shows a sample style sheet with the selector br:before (though browsers have ignored this, implementing br their own way). It can be argued that more and more elements have moved into the scope of CSS rendering, at least in part. For example, an input element (incuding its font, colors, etc.) is largely controllable with CSS in modern browsers.

Current browsers (Firefox, IE, Chrome) do not seem to support the :after and :before pseudo-elements for empty elements other than hr. For hr, IE and Chrome place the generated content inside a bordered box, which is the implementation of hr; the content makes the box taller. Firefox places the content of both (!) pseudo-elements after the horizontal rule that is its implementation of hr. This variation illustrates the kinds of “interaction” problems that are referred to in CSS 2.1.

It is often claimed that these pseudo-elements cannot be used for empty elements since their HTML definitions do not allow any content. This is a category error. The syntax rules of a markup language do not restrict what you can do in CSS

To conclude, :after and :before are currently not usable for empty elements (except marginally for hr), but this is mainly due to implementations and may change in the future.

Jukka K. Korpela
  • 178,198
  • 33
  • 241
  • 350
0

I've spent several hours plucking out my hair only to find that some other css override content (or display:none) property of my selector.

For example, if the following code is written in some other place, before or after element will never show:

#id > child:before {
  content: none!important;
}
<html>
<div id="id" class="class">
  <child>
    Before element is not showing
  </child>

</div>
<style>
  child:before {
    content: 'before';
    color: 'red';
  }
</style>

</html>

Just find the css which is overwriting your style and spam stronger selectors and !important to make it work

#id>child:before {
  content: none!important;
}
<html>
<div id="id" class="class">
  <child>
    Before element is <strong>showing</strong>
  </child>

</div>
<style>
  #id.class>child:before {
    content: 'before'!important;
    border: 1px solid red;
  }
</style>

</html>
glinda93
  • 3,643
  • 2
  • 16
  • 39
-1

Elements that doesn't have closing tag are void elements and they can't display content inside them:

https://www.w3.org/TR/html5/syntax.html#void-elements

All Blink, Webkit and Quantum browsers allow you to create pseudo elements only on checkboxes but this is controversial since no spec allow this behavior.

Here an example: https://codepen.io/equinusocio/pen/BOBaEM/

input[type="checkbox"] {
  appearance: none;
  color: #000;
  width: 42px;
  height: 24px;
  border: 1px solid currentColor;
  border-radius: 100px;
  cursor: pointer;
  transition: all 100ms;
  background-size: 30%;
  outline: none;
  position: relative;
  box-sizing: border-box;
  background-color: #eee;
  transition: background-color 200ms;

  &::before {
    content: '';
    position: absolute;
    left: 2px;
    top: 2px;
    bottom: 2px;
    height: 18px;
    width: 18px;
    border-radius: 50%;
    background-color: currentColor;
    will-change: transform;
    transition: transform 200ms cubic-bezier(.01,.65,.23,1);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  }

  &:checked {
    background-color: aquamarine;

    &::before {
      transform: translateX(100%);
    }
  }
}
Mattia Astorino
  • 1,084
  • 10
  • 17
  • it won't work in all the browser ... and there is no specification that says *now we can* .. if you have one please share it because it's not safe to consider pseudo element with input – Temani Afif Aug 18 '18 at 14:55
  • It works in all browsers except IE. Even flexbox is not supported by IE 9 so where is the problem. This solution works on all Blink, Webkit and Quantum browsers and can be very useful to people who work on webkit-only environment like Electron. – Mattia Astorino Aug 19 '18 at 16:08
  • Like you said in your answer, there is no spec for it, so one day it may stop working. It's not the only thing that behave and works without any spec defined but we should never rely on them and say that they works. it's like obselete tags, they still work but we should stop using them because one day they will not. So yes this can be useful to people and my comments are also useful to warn people that they should be aware that this not official. – Temani Afif Aug 19 '18 at 19:06