Can I have multiple :before pseudo-elements for the same element?
Asked Answered
D

5

170

Is it possible to have multiple :before pseudos for the same element?

.circle:before {
    content: "\25CF";
    font-size: 19px;
}
.now:before{
    content: "Now";
    font-size: 19px;
    color: black;
}

I am trying to apply the above styles to the same element using jQuery, but only the most recent one is applied, never both of them.

Doak answered 17/8, 2012 at 1:15 Comment(1)
Technically it should be possible to do something like .my_element::after {content: "main";} and .my_element::after::before {content: "sub";}. But sadly this does not work. The idea was to add another pseudo element (sub) on top of the main pseudo element (main).Mutazilite
I
136

In CSS2.1, an element can only have at most one of any kind of pseudo-element at any time. (This means an element can have both a :before and an :after pseudo-element — it just cannot have more than one of each kind.)

As a result, when you have multiple :before rules matching the same element, they will all cascade and apply to a single :before pseudo-element, as with a normal element. In your example, the end result looks like this:

.circle.now:before {
    content: "Now";
    font-size: 19px;
    color: black;
}

As you can see, only the content declaration that has highest precedence (as mentioned, the one that comes last) will take effect — the rest of the declarations are discarded, as is the case with any other CSS property.

This behavior is described in the Selectors section of CSS2.1:

Pseudo-elements behave just like real elements in CSS with the exceptions described below and elsewhere.

This implies that selectors with pseudo-elements work just like selectors for normal elements. It also means the cascade should work the same way. Strangely, CSS2.1 appears to be the only reference; neither css3-selectors nor css3-cascade mention this at all, and it remains to be seen whether it will be clarified in a future specification.

If an element can match more than one selector with the same pseudo-element, and you want all of them to apply somehow, you will need to create additional CSS rules with combined selectors so that you can specify exactly what the browser should do in those cases. I can't provide a complete example including the content property here, since it's not clear for instance whether the symbol or the text should come first. But the selector you need for this combined rule is either .circle.now:before or .now.circle:before — whichever selector you choose is personal preference as both selectors are equivalent, it's only the value of the content property that you will need to define yourself.

If you still need a concrete example, see my answer to this similar question.

The legacy css3-content specification contains a section on inserting multiple ::before and ::after pseudo-elements using a notation that's compatible with the CSS2.1 cascade, but note that that particular document is obsolete — it hasn't been updated since 2003, and no one has implemented that feature in the past decade. The good news is that the abandoned document is actively undergoing a rewrite in the guise of css-content-3 and css-pseudo-4. The bad news is that the multiple pseudo-elements feature is nowhere to be found in either specification, presumably owing, again, to lack of implementer interest.

Illuminometer answered 17/8, 2012 at 2:57 Comment(5)
In the given case, if an element belongs to both class circle and to class now, both rules apply to the element’s :before pseudo-element, but normal CSS imply that only one of the content settings can take effect (by normal cascade rules). The point is that content does not accumulate.Falda
Turns out this question that I answered several months later is a duplicate of this one. I have since merged most of the information from the other answer, which basically expands on everything @Jukka has said above, into this one as it's getting far more traffic, on top of coming first.Illuminometer
After 13 years, the css-content-3 draft has finally been updated. The pseudo-elements section has definitely been removed in favor of css-pseudo-4, which doesn't allow multiple ::before nor ::after pseudo-elements.Hark
@Oriol: 12 years, actually. the FPWD of css-pseudo-4 was introduced last year, at which time css-content-3 had already been updated to point to the new spec.Illuminometer
@Illuminometer The css-content-3 editor draft at drafts.csswg.org was updated some time ago, but the working draft at w3.org was still the 2003 one until recently. I still had some hopes...Hark
C
47

If your main element has some child elements or text, you could make use of it.

Position your main element relative (or absolute/fixed) and use both :before and :after positioned absolute (in my situation it had to be absolute, don't know about your's).

Now if you want one more pseudo-element, attach an absolute :before to one of the main element's children (if you have only text, put it in a span, now you have an element), which is not relative/absolute/fixed.

This element will start acting like his owner is your main element.

HTML

<div class="circle">
    <span>Some text</span>
</div>

CSS

.circle {
    position: relative; /* or absolute/fixed */
}

.circle:before {
    position: absolute;
    content: "";
    /* more styles: width, height, etc */
}

.circle:after {
    position: absolute;
    content: "";
    /* more styles: width, height, etc */
}

.circle span {
    /* not relative/absolute/fixed */
}

.circle span:before {
    position: absolute;
    content: "";
    /* more styles: width, height, etc */
}
Contumelious answered 16/7, 2014 at 22:23 Comment(5)
While the selected answer is accurate, this was a viable work around for my case. I was able to simulate having multiple psuedo elements without needing to add more nodes to the DOM!Velvavelvet
@Velvavelvet so, could you please tell how did you simulate?Overtrade
@BbIKTOP sorry just saw your comment. In my case I was working on a modal that always had children so I ended up with something like: .modal:first-child:before { …Velvavelvet
@Velvavelvet To recollect a detail like this after more than 6 years, you have a great memory!Doak
@Doak it's possible Matt was working on the same app/site all this time if he was trying to make it compatible with IE5.5 ;)Cadi
S
7

I've resolved this using:

.element:before {
    font-family: "Font Awesome 5 Free" , "CircularStd";
    content: "\f017" " Date";
}

Using the font family "font awesome 5 free" for the icon, and after, We have to specify the font that we are using again because if we doesn't do this, navigator will use the default font (times new roman or something like this).

Starling answered 5/2, 2020 at 12:27 Comment(1)
Perfect, this was my exact use case! Is this standard syntax or a hack, though?Silique
C
0

You can also use an image/icon plus text in the content field

e.g.

p.album-title::after {
  content: url('https://...camera-icon-blue.png') ' View >';
  display: block;
  ...;
}
Czerny answered 21/6, 2021 at 4:13 Comment(0)
L
0

In ::after css set content:'any text' and add backgroun-image with svg text from external svg file url(anySvgText.svg) or inline svg code url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="30" width="200"><text x="0" y="15" fill="black" style="font-family: tahoma;">any second text</text></svg>')

Also you can use only svg instead content value. but you must set empty string (content: '') to display a ::after style

.circle:before {
    content: "\25CF";
    font-size: 19px;
    color: red;
    width: 200px;
    display: block;
    background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="30" width="200"><text x="0" y="15" fill="black" style="font-family: tahoma;">Now</text></svg>');
    background-position-x: 15px;
}
<div class="circle"></div>
Luna answered 30/6, 2022 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.