What's the Point of the ::Before and ::After Pseudo-Element Selectors in CSS

What’s the point of the ::before and ::after pseudo-element selectors in CSS?

The CSS2.1 spec says this about generated content:

In some cases, authors may want user agents to render content that does not come from the document tree. One familiar example of this is a numbered list; the author does not want to list the numbers explicitly, he or she wants the user agent to generate them automatically. Similarly, authors may want the user agent to insert the word "Figure" before the caption of a figure, or "Chapter 7" before the seventh chapter title. For audio or braille in particular, user agents should be able to insert these strings.

Basically the purpose is to minimize pollution of the content structure by "content" that is otherwise more suited as presentational elements, or better to be automated.

What is the purpose of :before :after pseudo selector without content?

/* apply a natural box layout model to all elements, but allowing components to change */
html {
box-sizing: border-box;
}

*, *:before, *:after {
box-sizing: inherit;
}

This is just a general formatting statement...it's not trying to insert content here so the content property isn't required.

Pseudo-elements don't fall under the univeral selector * for box-sizing so the property has to be specifically applied.

Why do the :before and :after pseudo-elements require a 'content' property?

The reason you need a content: '' declaration for each ::before and/or ::after pseudo-element is because the initial value of content is normal, which computes to none on the ::before and ::after pseudo-elements. See the spec.

The reason the initial value of content isn't an empty string but a value that computes to none for the ::before and ::after pseudo-elements, is twofold:

  1. Having empty inline content at the start and end of every element is rather silly. Remember that the original purpose of the ::before and ::after pseudo-elements is to insert generated content before and after the main content of an originating element. When there's no content to insert, creating an additional box just to insert nothing is pointless. So the none value is there to tell the browser not to bother with creating an additional box.

    The practice of using empty ::before and ::after pseudo-elements to create additional boxes for the sole purpose of layout aesthetics is relatively new, and some purists might even go so far as to call it a hack for this reason.

  2. Having empty inline content at the start and end of every element means that every (non-replaced) element — including html and body — would by default generate not one box, but up to three boxes (and more in the case of elements that already generate more than just the principal box, like elements with list styles). How many of the two extra boxes per element will you actually use? That's potentially tripling the cost of layout for very little gain.

    Realistically, even in this decade, less than 10% of the elements on a page will ever need ::before and ::after pseudo-elements for layout.

And so these pseudo-elements are made opt-in — because making them opt-out is not only a waste of system resources, but just plain illogical given their original purpose. The performance reason is also why I do not recommend generating pseudo-elements for every element using ::before, ::after.

But then you might ask: why not have the display property default to none on ::before, ::after? Simple: because the initial value of display is not none; it is inline. Having inline compute to none on ::before, ::after is not an option because then you could never display them inline. Having the initial value of display be none on ::before, ::after is not an option because a property can only have one initial value. (This is why the initial value of content is always normal and it is simply defined to compute to none on ::before, ::after.)

Why use * selector in combination with *::before and *::after

See these two JSFiddles:

http://jsfiddle.net/86gc1w6f/
http://jsfiddle.net/gwbp2vpL/1/

Or try these snippets:

CSS:

* {
box-sizing: content-box;
}

p {
position: relative;
width: 200px;
border: 10px solid rgba(255, 0, 0, 0.5);
}

p::after {
position: absolute;
right: -100px;
top: -10px;
width: 100px;
height: 30px;
border: 10px solid rgba(0, 0, 255, 0.5);
content: '';
}

HTML:

<p>
A paragraph
</p>

Changing the box-sizing between content-box and border-box only alters the size of the paragraph, not it's ::after element. As others have noted, this is because they are, as named, pseudo elements and must be targeted separately.

It would appear that * does not target psuedo-elements (which as @Harry points out, is as per the CSS specification)

when to use pseudo-classes and when to use pseudo-elements selectors in CSS

The single colon syntax is an older implementation. In general, there are pseudo-elements :: & pseudo-classes :, and they are not identical. In this case though, browsers still support the outdated single-colon syntax.

This means that in your example with :before/::before, it will not make a difference to the outcome, but in general you should use the double colon syntax, because before & after are pseudo-elements, not pseudo-classes.

Read more on MDN.

Can I use a :before or :after pseudo-element on an input field?

:after and :before are not supported in Internet Explorer 7 and under, on any elements.

It's also not meant to be used on replaced elements such as form elements (inputs) and image elements.

In other words it's impossible with pure CSS.

However if using jquery you can use

$(".mystyle").after("add your smiley here");

API docs on .after

To append your content with javascript. This will work across all browsers.

When to use :before or :after

The naming of ::before and ::after is not entirely arbitrary, but it only really makes sense when the content of those pseudo elements is displayed inline, just before or just after the content of the element they are attached to.

As soon as you use

position: absolute;

in a pseudo element (which is totally legitimate), it no longer matters whether that pseudo element is named ::before or ::after.

It might just as easily be named ::presentational-frill-1 or ::presentational-frill-2.

What is the difference between pseudo-classes and pseudo-elements?

From https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Pseudo-classes_and_pseudo-elements

Pseudo-class :

A CSS pseudo-class is a keyword, preceded by a colon (:), added to the end of selectors to specify you want to style the selected elements, and only when they are in certain state. For example, you might want to style an element only when it is being hovered over by the mouse pointer, or a checkbox when it is disabled or checked, or an element that is the first child of its parent in the DOM tree.

Examples:

  • :active
  • :checked
  • :nth-child()
  • :first
  • :hover

Pseudo-elements ::

Pseudo-elements are very much like pseudo-classes, but they have differences. They are keywords, this time preceded by two colons (::), that can be added to the end of selectors to select a certain part of an element.

Examples:

  • ::after
  • ::before
  • ::first-letter
  • ::first-line
  • ::selection
  • ::backdrop

As stated by @stephanmg:

In practice ::before is used as :before and ::after is used as :after
because of browser compatibility. Both are pseudo-elements, but may
look like pseudo classes. This might be confusing if you read CSS
code.

Selecting and manipulating CSS pseudo-elements such as ::before and ::after using javascript (or jQuery)

You could also pass the content to the pseudo element with a data attribute and then use jQuery to manipulate that:

In HTML:

<span>foo</span>

In jQuery:

$('span').hover(function(){
$(this).attr('data-content','bar');
});

In CSS:

span:after {
content: attr(data-content) ' any other text you may want';
}

If you want to prevent the 'other text' from showing up, you could combine this with seucolega's solution like this:

In HTML:

<span>foo</span>

In jQuery:

$('span').hover(function(){
$(this).addClass('change').attr('data-content','bar');
});

In CSS:

span.change:after {
content: attr(data-content) ' any other text you may want';
}


Related Topics



Leave a reply



Submit