Reputation: 75
I'm a web developer noob and hoping someone with more experience can help.
<div id="domain-background-container">
<div class="domain-hotspots">
<div id="hotspot">
<p class="bubble">I'm red on hover</p>
</div>
</div>
</div>
<div id="page-nav" >
<p>I'm red on hover</p>
</div>
html, body { /* <--- (1) */
height: 100%; }
#domain-background-container {
position: fixed;
z-index: -10; } /* <--- (2) */
.domain-hotspots {
width: 100vw;
height: 100vh;
position: fixed;
background: blue; }
#hotspot {
width: 2em;
height: 2em;
background: green; }
#hotspot:hover .bubble {
background: red; }
#page-nav {
width: 100%;
text-align: center;
background: yellow; }
#page-nav:hover p {
color: red; }
Here's the jsfiddle.
The problem is that #hotspot (that is the green square) doesn't enter the hover state when the mouse cursor is placed over it.
I'm looking to have four things explained:
I'd really appreciate any knowledge people can share on this! I'm stumped. I'm not even sure what I should google for to research the issue, as I don't know what's causing this behaviour.
Upvotes: 6
Views: 263
Reputation: 78870
The rules governing how this all works is a bit complex; if you really want to look into it, see Appendix E. Elaborate description of Stacking Contexts from the CSS spec. As to your specific questions:
The hotspot is not triggered because #domain-background-container
is at a negative z-index
, so the body, which has the default z-index
, covers it and "blocks" the pointer events. You can see this in action if you go into the developer tools and use the "click to select an element" feature; the body is highlighted instead of the hotspot because it's on top.
If you remove the height of the body, because the body only contains positioned elements, the body's natural height is now 0, so it no-longer blocks the background. An element only has height if you give it one or if it contains "flowing" elements (not absolute or fixed) that lend it height.
Yes, playing the z-index game does not scale well in a project; keep it to a minimum and try to use natural stacking whenever possible.
It's not clear what your intention is with your page's layout, but there are multiple potential solutions. You can use pointer-events: none
on elements where you want the mouse events to "pass through" to a lower level, for example.
Sample:
body {
pointer-events: none;
}
#domain-background-container {
pointer-events: auto;
}
Upvotes: 4
Reputation: 23870
Can someone provide or point me to a detailed explanation of the mechanics preventing
#hotspot
from entering the hover state?
<body>
is covering it up.
This can be seen by using the "Inspect element" function of a browser's dev tools.
(Due to the way z-index
works though, giving it a background
does not put that background in front of the div.)
Removing CSS ruleset (1) [height: 100% for html and body] allows
#hotspot
to enter the hover state. This isn't a viable solution in my project, but for curiosity's stake, why does this make a difference.
With height: 100%
:
Without height: 100%
:
[...] to my understanding elements with unmodified z-index which follow .domain-hotspots in the html file should be rendered on top of
.domain-hotspots
in the viewport.
From the CSS Level 2 draft, § 9.9.1:
Within each stacking context, the following layers are painted in back-to-front order:
- the background and borders of the element forming the stacking context.
- the child stacking contexts with negative stack levels (most negative first).
- the in-flow, non-inline-level, non-positioned descendants.
- the non-positioned floats.
- the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
- the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
- the child stacking contexts with positive stack levels (least positive first).
#page-nav
is category 3 due to the default display: block
on any <div>
.#domain-background-container
is category 2 due to position: fixed
, which removes it from the flow and creates a child stacking context, and due to its negative z-index
.Category 3 gets rendered over category 2, so #page-nav
is in front of #domain-background-container
.
Now when you remove the z-index
from #domain-background-container
, or make it 0 or positive, it becomes category 6 or 7, thus being rendered after (i.e. over) #page-nav
.
Theoretically I could position and raise the z-value of #page-nav. While that would work for this simple jsfiddle I'm loath to do that for a bigger project where I'd have to apply that to every element following .domain-hotspots. [...] I can't use the two "solutions" I just mentioned, so what solution remains?
You don't actually have to raise its z-value
, you merely have to position it. position: relative
will do, even.
And applying that to every element following #domain-background-container
is no miracle either:
#domain-background-container ~ * {
position: relative;
}
Works quite well:
html, body { /* <--- (1) */
height: 100%; }
#domain-background-container {
position: fixed;
/*z-index: -10;*/ } /* <--- (2) */
#domain-background-container ~ * {
position: relative; }
.domain-hotspots {
width: 100vw;
height: 100vh;
position: fixed;
background: blue; }
#hotspot {
width: 2em;
height: 2em;
background: green; }
#hotspot:hover .bubble {
background: red; }
#page-nav {
width: 100%;
text-align: center;
background: yellow; }
#page-nav:hover p {
color: red; }
<div id="domain-background-container">
<div class="domain-hotspots">
<div id="hotspot">
<p class="bubble">I'm red on hover</p>
</div>
</div>
</div>
<div id="page-nav" >
<p>I'm red on hover</p>
</div>
Admittedly though, there are situations where it is not feasible to position all those elements.
Another solution though, is to simply position the body
element, which then allows you to give it a z-index
of its own:
body {
position: relative;
z-index: -11;
}
Demo:
html, body { /* <--- (1) */
height: 100%; }
body {
position: relative;
z-index: -11; }
#domain-background-container {
position: fixed;
z-index: -10; } /* <--- (2) */
#domain-background-container ~ * {
position: relative; }
.domain-hotspots {
width: 100vw;
height: 100vh;
position: fixed;
background: blue; }
#hotspot {
width: 2em;
height: 2em;
background: green; }
#hotspot:hover .bubble {
background: red; }
#page-nav {
width: 100%;
text-align: center;
background: yellow; }
#page-nav:hover p {
color: red; }
<div id="domain-background-container">
<div class="domain-hotspots">
<div id="hotspot">
<p class="bubble">I'm red on hover</p>
</div>
</div>
</div>
<div id="page-nav" >
<p>I'm red on hover</p>
</div>
Upvotes: 5
Reputation: 62596
#domain-background-container
to z-index: -10
, you actually put that div behind the body
tag, so you hover on the body
tag and not on the #hotspot
tag.body
& html
elements, the body
element's height will actually be height of the #page-nav
element (because the other element is positioned as fixed
, so it will not take height of the page) - hence - you can hover it, because there is no other element that sits "before" it - z-index based.#nav-page
to relative
and remove the z-index
on the #domain-background-container
.Here is an example:
html, body {
height: 100%;
}
#domain-background-container {
position: fixed;
}
.domain-hotspots {
width: 100vw;
height: 100vh;
position: fixed;
background: blue;
}
#hotspot {
width: 2em;
height: 2em;
background: green;
}
#hotspot:hover .bubble {
background: red;
}
#page-nav {
width: 100%;
text-align: center;
background: yellow;
position: relative;
}
#page-nav:hover p {
color: red;
}
<div id="domain-background-container">
<div class="domain-hotspots">
<div id="hotspot">
<p class="bubble">I'm red on hover</p>
</div>
</div>
</div>
<div id="page-nav" >
<p>I'm red on hover</p>
</div>
Upvotes: 5