11

I have a fixed top navbar with dropdowns. I want to slide the dropdown menus in on hover. I want that the menu is behind the navbar while sliding in. So I've simply tried to set the z-index of both elements which unfortunately did not work for me.

Here a simplified example (codepen)

html

<div class="fixed-top">
  <span class="trigger">hover me</span>
  <div class="menu"></div>
</div>

css

.fixed-top {
  background: #3b5999;
  height: 50px;
  width: 100%;
  padding: 0;
  top: 0;
  position: fixed;
  z-index: 2;
}

.trigger {
  z-index: 2;
  font-size: 33px;
  color: white;
  margin-left: 50%;
}

.trigger:hover + .menu {
  margin-top: 0;
}

.menu {
  z-index: 1;
  height: 300px;
  background-color: #1c7754;
  width: 400px;
  margin: auto;
  margin-top: -400px;
  transition: all 0.75s ease-out;
}

In case it's not clear what I want to do here a simple mspaint sketch ;)

mspaint skills confirmed

boop
  • 6,033
  • 9
  • 37
  • 76

3 Answers3

8

This is an extremely common error when beginning to work with stacking contexts in CSS. Basically, you just have to remember that a child cannot exist in a different stacking context from a parent.

So if I have a non-static element (meaning an element with position: anything-but-static [fixed, relative, absolute]), and if that element has no non-static parent element, then it will be at stacking context level 1, no matter where it is in the DOM. Now if that element has a non-static child element, that child will be at stacking context level 2. It cannot be on the same level (level 1) as its parent element. z-index can only affect elements on the same stacking context level, it has nothing to do with elements on different stacking context levels.

The solution is to restructure your HTML, or just use a :before or :after pseudo-element, thus:

.fixed-top {
    height: 50px;
    width: 100%;
    padding: 0;
    top: 0;
    position: fixed; /* The parent: stacking context level 1 */
}
.fixed-top:before {
    background: #3b5999;
    content:'';
    position: absolute; /* stacking context level 2 */
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: 1;
}
.trigger {
    color: white;
    font-size: 33px;
    margin-left: 50%;
    position: relative; /* also stacking context level 2 */
    z-index: 2;
}
.trigger:hover + .menu {
    margin-top: 0;
}
.menu { /* bottom layer -- no stacking context necessary */
    z-index: 0;
    height: 300px;
    background-color: #1c7754;
    width: 400px;
    margin: auto;
    margin-top: -400px;
    transition: all 0.75s ease-out;
}

Note the comments denoting the stacking context levels.

And here's a JSFiddle for an example.

bowheart
  • 4,340
  • 1
  • 21
  • 23
  • Nice, still need to change it into this: `.trigger:hover + .menu, .menu:hover` so that the menu won't slide back up when the mouse leaves the `span`, +1 – Mi-Creativity Nov 25 '15 at 05:10
  • @Mi-Creativity I cannot tell that that is part of the spec, but I'll give you kudos for the suggestion. – bowheart Nov 25 '15 at 05:13
  • This is the answer I was looking for. I am seeing these symptoms now and after some messing with z-index, I started to feel strongly that children could never go under the parent. I haven't solved it yet, but between your post and your examples, I should be able to. – agm1984 Nov 16 '17 at 23:59
4

Try running the Code Snippet.

.fixed-top {
  background: #3b5999;
  height: 50px;
  width: 100%;
  padding: 0;
  top: 0;
  position: fixed;
  z-index: 2;
}

.trigger {
  font-size: 33px;
  color: white;
  margin-left: 50%;
  width: 100%;
  height: 50px;
  top: 0;
  position: fixed;  
  z-index: 3;  
}

.trigger:hover + .menu,
.menu:hover{
  margin-top: 0;
}

.menu {
  z-index: 1;
  height: 300px;
  background-color: #1c7754;
  width: 400px;
  margin: auto;
  margin-top: -400px;
  transition: all 0.75s ease-out;
}
<div class="fixed-top"></div>
<span class="trigger">hover me</span>
<div class="menu"></div>
kolunar
  • 2,623
  • 3
  • 24
  • 35
  • This is working, thank you. But tbh I'm not very happy with it.. :D [related question](http://stackoverflow.com/questions/33908787/is-there-a-css-selector-which-selects-an-element-outside-the-current-element) – boop Nov 25 '15 at 05:04
  • Well, would be surprised if someone could achieve it without compromising your html's skeleton :) – kolunar Nov 25 '15 at 05:21
  • @kolunar Umm, you mean like how I did in my answer? – bowheart Nov 25 '15 at 05:23
  • @bowheart Ah! you nailed it, the pseudo :before masking does the trick :D – kolunar Nov 25 '15 at 05:30
0

i think this is what you want.

css code

    .fixed-top {
  background: #3b5999;
  height: 50px;
  width: 100%;
  padding: 0;
  top: 0;
  position : fixed;
}

.trigger {
  z-index: 500;
  font-size: 33px;
  color: white;
  margin-left: 50%;
  position : absolute;
}

.trigger:hover + .menu {
  margin-top: 0;
}

.menu {
  z-index: -9999;
  position : relative;
  height: 300px;
  background-color: #1c7754;
  width: 400px;
  margin: auto;
  margin-top: -400px;
  transition: all 0.75s ease-out;
}

    <div class="fixed-top">
  <span class="trigger">hover me</span>
  <div class="menu"></div>
</div>

here is the reference that you can check

http://www.smashingmagazine.com/2009/09/the-z-index-css-property-a-comprehensive-look/

Sagar Pawar
  • 405
  • 4
  • 22