You basically answered your own question. ::before
and ::after
are pseudo-elements. The name even implies that they don't represent true elements.
The selector *, *::before, *::after
is meant to match:
*
All elements present in the DOM
*::before
The pseudo element ::before
relative to all elements present in the DOM
*::after
The pseudo element ::after
relative to all elements present in the DOM
Since pseudo-elements aren't qualified as full elements (<html>
, <head>
, <body>
, etc.) and aren't part of the DOM, they are not matched by an asterisk character. They are only matched when selected in reference to another element. They can't be selected in JavaScript for the same reason. JavaScript only selects elements that it can find in the DOM, which is not true in reference to pseudo elements.
To quote from MDN:
You can use only one pseudo-element in a selector. It must appear after the simple selectors in the statement.
A simple selector is defined by W3C using the following definition:
A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.
Simple selectors are meant to match elements present in the DOM. A pseudo element must follow a simple selector since it is not part of the DOM itself, and consequently, can not be classified as an element. Since *
is a simple selector, specifically the universal selector, pseudo elements in CSS can be chained to it to match all of that particular pseudo element type. Without the pseudo element selector, only a simple selector would remain (*
).