Why Use * Selector in Combination with *::Before and *::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)

Universal selector * and pseudo elements

No, the universal selector * does not affect pseudo-elements (except indirectly via inheritance, as pseudo-elements are typically generated as children of actual elements).

The universal selector, like other named element selectors such as p and div, is a simple selector:

A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.

A simple selector, and by extension any complex selector, targets only actual elements.

Although pseudo-elements (which are not the same thing as pseudo-classes mentioned above) can appear in selector notation alongside simple selectors, pseudo-elements are completely separate from simple selectors as they represent abstractions of the DOM that are separate from actual elements, and therefore both represent different things. You cannot match a pseudo-element using a simple selector, nor can you apply styles to an actual element in a CSS rule with a pseudo-element in its selector.

So, in order to match :before and :after pseudo-elements of any element, yes, one will need to include *:before, *:after in the selector. Having just * { box-sizing: border-box; } will not affect them since box-sizing is not normally inherited, and as a result, they will retain the default box-sizing: content-box.

One possible reason why you might never have had any issues with pseudo-elements is that they're displayed inline by default, as box-sizing has no effect on inline elements whatsoever.

Some notes:

  • As with any other chain of simple selectors, if * is not the only component then you can leave it out, which means *, :before, :after is equivalent to *, *:before, *:after. That being said, the * is usually included for the sake of clarity — most authors are used to leaving the * out when writing ID and class selectors, but not pseudo-classes and pseudo-elements, so the notation may seem strange and even wrong to them (when it is in fact perfectly valid).

  • The current Selectors specification that I link to above represents pseudo-elements with double colons. This is a new notation introduced in the current spec to distinguish pseudo-elements from pseudo-classes, but most box-sizing resets use the single colon notation to accommodate IE8, which supports box-sizing but not the double colon notation.

  • Although *:before, *:after applies styles to the respective pseudo-elements of any element, which includes html, head and body, the pseudo-elements will not actually be generated until you apply the content property. You do not have to worry about any performance issues as there are none. For a detailed explanation, see my answer to this related question.

What's the purpose of these CSS rules?

This snippet has a few purposes, mostly just overriding/resetting some default styling. The * selector applies these values to every element on the page, and the ::before/::after make sure it also applies to any pseudo-elements.

Specifically...

  1. It removes all padding and margin from elements that have them applied by default. For example, <ul> elements:

<ul><li>I have a margin</li></ul>

Select all pseudo elements and classes

* selects any element regardless of its nature or state. In this way, it already covers all pseudo-classes, just with zero specificity.

For example, * will match any element regardless of whether it is :first-child, :last-child, or both (which itself can be expressed using either :only-child or :first-child:last-child). It will also match any link whether it is unvisited (:link) or visited (:visited), and whether or not it matches one or more of :hover/:active/:focus.

If you're looking for a way to override any and all CSS rules with pseudo-classes for a given element (which can be useful in the case of dynamic pseudo-classes such as the one for links), the only ways are to use an ID selector, an inline style attribute, or !important.

* doesn't match pseudo-elements because it is a simple selector, and a simple selector only matches actual elements. See my answer to this question for a detailed explanation.

The likely reason that there isn't a selector for matching all pseudo-elements is because it doesn't make sense to have one, since different pseudo-elements work differently and have different restrictions as to which CSS properties may be applied to them. For example, content and display don't apply to ::first-letter, ::first-line or ::selection. But the universal selector exists because elements themselves don't define what CSS properties are applicable (not usually, anyway); as far as CSS is concerned, every element is more or less equal.

Adding functionality to any TextReader

I agree that creating a wrapper that implements TextReader, delegates the implementation to the underlying TextReader and adds some additional support for tracking of location is a good approach. You can think of this as the Decorator pattern, because you're decorating the TextReader class with some additional functionality.

Better wrapping: You can write it in a simpler way to decouple your TextReader from the Location type - this way you can easily modify Location independently or even provide other features that are based on tracking the progress of reading.

interface ITracker {
void AdvanceString(string s);
}

class TrackingReader : TextReader {
private TextReader source;
private ITracker tracker;
public TrackingReader(TextReader source, ITracker tracker) {
this.source = source;
this.tracker = tracker;
}
public override int Read(char[] buffer, int index, int count) {
int res = base.Read(buffer, index, count);
tracker.AdvanceString(buffer.Skip(index).Take(res);
return res;
}
}

I would also encapsulate creation of the tracker into some factory method (so that you have a single place in the application that deals with construction). Note that you can use this simple desing to create a TextReader that reports the progress to multiple Location objects too:

static TextReader CreateReader(TextReader source, params ITracker[] trackers) {
return trackers.Aggregate(source, (reader, tracker) =>
new TrackingReader(reader, tracker));
}

This creates a chain of TrackingReader objects and each of the object is reporting the progress of reading to one of the trackers passed as arguments.

Regarding Event-based: I think that using standard .NET/C# events for this kind of thing isn't done as frequently in the .NET libraries, but I think this approach may be quite interesting too - especially in C# 3 where you can use features like lambda expressions or even Reactive Framework.

Simple use doesn't add that much boiler plate:

using(ReaderWithEvents reader = new ReaderWithEvents(source)) {
reader.Advance += str => loc.AdvanceString(str);
// ..
}

However, you could also use Reactive Extensions to process the events. To count the number of new lines in the source text, you could write something like this:

var res =
(from e in Observable.FromEvent(reader, "Advance")
select e.Count(ch => ch == '\n')).Scan(0, (sum, current) => sum + current);

This would give you an event res that fires each time you read some string from the TextReader and the value carried by the event would be the current number of line (in the text).



Related Topics



Leave a reply



Submit