CSS has a multitude of relational selectors, none of which let you target a parent from a child, at least not as of today. To be fair, not having a parent selector is not the most inhibiting of problems necessarily; but there are situations where having one might spare us from depending on Javascript modifications. Of course, this is only going to be appealing if you also hold the view that little dependence on javascript is a good thing.
The problem
Consider the following scenario: You need to change the background-color
of <body>
to hotpink
when the mouse hovers upon an inner element say, a <button>
. This isn't possible without resorting to using a script to modify <body>
, with said script set to trigger when the mouse hovers over <button>
. There isn't an ideal pure CSS relational selectors solution, but let me introduce you to one involving pointer-events
. It's pretty much today-ready as the caniuse coverage is quite decent, so it's worth learning about, if nothing else.
Pointer events?
A brief explanation of what pointer-event
is first and then we can use it to solve our problem. Lingo-wise, here's some context on a pointer via the MDN docs — "A pointer is a hardware agnostic representation of input devices (such as a mouse, pen or contact point on a touch-enable surface) ..." And so unsurprisingly, a pointer event is the event that transpires when that contact is made. CSS provides pointer-events
to dictate what can happen when an element receives such an event. I feel like I'm making this more complicated than it needs to be, so here's a super quick example from the MDN docs of leveraging that selector to make a link "unclickable": a {pointer-event: none}
. The default is auto
which allows pointer events to flow through but none
blocks them right then and there and thus for all intents and purposes makes the link "unclickable". There's a bit more to them, especially involving SVGs, but I got a mission to get you a solution, so read them docs later.
The solution
Given this simple DOM structure:
<body>
<button>Submit</button>
</body>
we need to change body
's background-color
property on button:hover
. Here's the solution followed by an explanation:
body {
pointer-events: none; /* 1 */
}
button {
pointer-events: auto; /* 2 */
}
body:hover { /* 3 */
background-color: hotpink;
}
We begin by making the parent element
body
unresponsive to pointer events with:body { pointer-events: none }
and at this point abody:hover
selector would not work. But note something important here -pointer-events: none
is applied to every single child element insidebody
here onwards, because they implicitly inherit thatnone
as well.So we are at a point where
button:hover
doesn't respond now either; but we need it to, hence:button { pointer-events: auto; }
and thebutton:hover
selector is back in play again.Finally we set
body:hover { background-color: hotpink; }
and that works because, here's the thing,body-hover
now only happens withbutton:hover
becausebody
's pointer-events-related-selectors now only trigger on the first child's pointer events.
</solution>
Sure, this is not the most elegant of solutions, and has a bunch of caveats, even in this simple application here — but you do now know, at least in certain cases, how to affect a parent element in CSS using pointer events! Thanks for reading!
Top comments (0)