You cannot use a combinator to target a pseudo-element relative to elements other than its generating element.
This is because they're pseudo-elements, not actual elements, and combinators only work by establishing relationships between actual elements. A pseudo-element, on the other hand, can only be applied to the subject of a selector (the rightmost compound selector), and this happens only after matching is processed on the real elements. In other words, matching is done first as though the pseudo-element wasn't there, then the pseudo-element, if it's indicated within the selector, is applied to each match.
In your code, the following selector:
a[href^="http"] img ~ :after
Does not actually look for an :after
pseudo-element that comes after an img
within the a
, even though it appears that way as both are rendered as children of the a
element.
It can be rewritten into the following:
a[href^="http"] img ~ *:after
Notice the *
selector, which is implied. Similarly to how you can omit *
before any other simple selectors for it to be implied, omitting *
from a pseudo-element also makes it implied to be there. See the spec for details.
Now, even though it appears *:after
should still match a:after
(since a
would match *
), it still doesn't work that way. If you remove the :after
pseudo-element from the selector:
a[href^="http"] img ~ *
You'll notice that the meaning of the selector changes entirely:
Select any element
that appears as a following sibling of an img
that is a descendant of an a
(whose href
starts with "http").
Since the img
is the last child of the a
element in your HTML, there are no following siblings to match, and therefore no :after
pseudo-elements can be generated.
In the case of a :before
or :after
pseudo-element, one might think of matching the pseudo-element's generating element relative to the pseudo-element's "sibling", but as the OP has correctly pointed out, there is no parent selector, so they're out of luck there, too.