Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hover, z-index and layering... a convoluted mess?

Tags:

html

css

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:

  1. Can someone provide or point me to a detailed explanation of the mechanics preventing #hotspot from entering the hover state?
  2. 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.
  3. Removing the the z-index of CSS ruleset (2) also allows #hotspot to enter the hover state. Unfortunately, this also screws up z-ordering as .domain-hotspots (blue) then covers #page-nav (yellow) which isn't acceptable. 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 (which shouldn't even be necessary, because 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) :-/
  4. I can't use the two "solutions" I just mentioned, so what solution remains?

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.

like image 829
assailant Avatar asked Sep 25 '16 00:09

assailant


People also ask

Does Z index affect hover?

If an element is transparent and the element with z-index:-1; is 'under' it. This stops the hover effects. Z-index can you see as elevations in a building, and you watching it from birdseye. You can't reach the basement if there is a floor above it, even if its build from glass.

What is the purpose of the Z index and how is it used?

The z-index CSS property sets the z-order of a positioned element and its descendants or flex items. Overlapping elements with a larger z-index cover those with a smaller one.

Why Z index is not working?

z-index only works on positioned elements (relative, absolute, fixed, sticky) so if you set a z-index on an element with a static position, it won't work.


1 Answers

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%:

screenshot

Without height: 100%:

screenshot

[...] 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:

  1. the background and borders of the element forming the stacking context.
  2. the child stacking contexts with negative stack levels (most negative first).
  3. the in-flow, non-inline-level, non-positioned descendants.
  4. the non-positioned floats.
  5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
  6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
  7. 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>
like image 188
Siguza Avatar answered Sep 30 '22 00:09

Siguza