0

I'm trying to make a drop-down box with a button and a toolbar so that my drop-down will be behind the toolbar but in front of my main element. The problem is that to do this I use z-index with negative value because using positive value on the toolbar will cause it to get behind my drop-box due to positioning and hierarchy.

I know that if I'll go and change the z-index of elements in my DOM to smaller negative values (as suggested here) it will make my links clickable again, but this seems to be a very inefficient way of solving the issue. Plus, I'd rather not mess with the toolbar and break it different elements for each button as this can cause a lot of issues with responsiveness in the future. Please take a look at the JFiddler link below.

Here is my code:

$('#btn2').on('click', function() {
  console.log('Click!');
  var element = document.getElementById("sub-memu");
  element.classList.toggle("show");
})
body {
  background-color: gray;
  height: 100vh;
  padding: 0px;
  margin: 0px;
}

.toolbar {
  height: 40px;
  background-image: linear-gradient(white, red);
  box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.35);
  position: relative;
}

.buttons {
  display: flex;
  flex-direction: row;
}

button {
  margin-right: 5px;
}

#sub-memu {
  position: absolute;
  display: flex;
  flex-direction: column;
  background-color: white;
  padding: 10px;
  min-width: 100px;
  z-index: -1;
  transform: translate(0px, -140%);
  transition: transform 1s;
}

main {
  padding: 10px;
  margin: 10px;
  background-color: yellow;
  height: calc(100% - 40px - 20px - 20px);
  position: relative;
  z-index: -2;
}

.show {
  transform: translate(0px, 25px) !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
  <div class='toolbar'>
    <div class='buttons'>
      <button id='btn1'>
        Button 1
      </button>
      <div class='btn-wrapper'>
        <button id='btn2'>
          Button 2
        </button>
        <div id='sub-memu' class='subMenu'>
          <a href='#'>Can you click me?</a>
          <a href='#'>Can you click me?</a>
          <a href='#'>Can you click me?</a>
          <a href='#'>Can you click me?</a>
        </div>
      </div>
    </div>
  </div>
  <main>
    This is my main element.
    <a href="#">You should be able to click me</a>.
  </main>
</body>

https://jsfiddle.net/atb8yq6e/1/

What I'm trying to achieve is the same behavior as can bee seen in my JSFiddle link (drop-down box lowering from behind the navigation bar but in front of my main content) but with active links, without going through my DOM tree and changing every overlapping element's z-index to lower number.

Thank you all in advance, Lior.

Lior Lavi
  • 133
  • 1
  • 9
  • what the problem change `z-index` in `#sub-memu` to `z-index:1` ? – Vadim Hulevich Aug 12 '19 at 09:24
  • @VadimHulevich Hi Vadim, doing as you suggested will cause the the menu to move in front of the navigation bar instead of behind it. Please see my question "... drop-down box lowering _from behind the navigation bar_ but in front of my main content ..." – Lior Lavi Aug 12 '19 at 09:35

3 Answers3

1

Add z-index to body element to make sure you create a stacking context and avoid having the element going behind it:

$('#btn2').on('click', function() {
  console.log('Click!');
  var element = document.getElementById("sub-memu");
  element.classList.toggle("show");
})
body {
  background-color: gray;
  height: 100vh;
  padding: 0px;
  margin: 0px;
  position:relative;
  z-index: 0;
}

.toolbar {
  height: 40px;
  background-image: linear-gradient(white, red);
  box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.35);
  position: relative;
}

.buttons {
  display: flex;
  flex-direction: row;
}

button {
  margin-right: 5px;
}

#sub-memu {
  position: absolute;
  display: flex;
  flex-direction: column;
  background-color: white;
  padding: 10px;
  min-width: 100px;
  z-index: -1;
  transform: translate(0px, -140%);
  transition: transform 1s;
}

main {
  padding: 10px;
  margin: 10px;
  background-color: yellow;
  height: calc(100% - 40px - 20px - 20px);
  position: relative;
  z-index: -2;
}

.show {
  transform: translate(0px, 25px) !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
  <div class='toolbar'>
    <div class='buttons'>
      <button id='btn1'>
        Button 1
      </button>
      <div class='btn-wrapper'>
        <button id='btn2'>
          Button 2
        </button>
        <div id='sub-memu' class='subMenu'>
          <a href='#'>Can you click me?</a>
          <a href='#'>Can you click me?</a>
          <a href='#'>Can you click me?</a>
          <a href='#'>Can you click me?</a>
        </div>
      </div>
    </div>
  </div>
  <main>
    This is my main element.
    <a href="#">You should be able to click me</a>.
  </main>
</body>

Related: Why can't an element with a z-index value cover its child?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Hi Temani, I tried your answer and it seems to work, although it is a bit on the side of creating stacking context and assigning z-index and positioning for every relevant element in the hierarchy, which is something I hoped to avoid. Do you have an opinion on Saeed Taran's suggestion below? – Lior Lavi Aug 12 '19 at 10:26
  • @LiorLavi *it is a bit on the side of creating stacking context and assigning z-index and positioning for every relevant element in the hierarchy* --> I only added z-index and position to body element, that's all. Why you need to do this for other elements? – Temani Afif Aug 12 '19 at 10:39
  • In my actual code the structure of the DOM is more complex and has more levels so simply adding the z-index to the body doesn't seem to cut it. But adding it to the relevant elements such as the #app and my .container-fluid indeed solved it. – Lior Lavi Aug 12 '19 at 11:10
  • @LiorLavi *In my actual code the structure of the DOM is more complex* --> if we don't have your **real** code then we cannot provide you with an accurate solution. I provided the best solution considering the code I have and this doesn't mean it's the best solution for any kind of code or DOM structure. If I see your real code I may provide a complete different solution. – Temani Afif Aug 12 '19 at 11:13
  • You are absolutely right. The thing is my code is part of a Laravel project and l didn't think it make sense to drop it all here as it split across multiple files. – Lior Lavi Aug 12 '19 at 12:39
0

add this codes to your css:

html{
  pointer-events:none;
}
body *{
  pointer-events:initial;
}
body {
  background-color: gray;
  height: 100vh;
  padding: 0px;
  margin: 0px;
  pointer-events:none;
}

https://jsfiddle.net/hL32e6cz/

Saeed Taran
  • 376
  • 2
  • 14
  • Hi Saeed, I tried your suggestion and while it works on JSFiddler, it doesn't seem to be the case in my actual code :( For some reason, in my actual code it still seems that the underlining elements overlap and cover my child elements. I tried Temani Afif's suggestion and it seems to work, although your solution is cleaner. Too bad I can't make it work in my project as well :( – Lior Lavi Aug 12 '19 at 10:20
  • this should work because the only thing this code do is making the body and html tags to transfer click event to their behind where your negative zindex elements are placed – Saeed Taran Aug 12 '19 at 10:36
  • Interesting.. I'll branch out my git branch and test it again as soon as I get the chance and update you on the results. Thank you! :) – Lior Lavi Aug 12 '19 at 11:13
-1

Simply set z-index to 1 instead on your #sub-memu (should probably be menu?), and remove the z-index value on main.

André Ekeberg
  • 307
  • 3
  • 13
  • Hi Tidsoptimisten, doing as you suggested will cause the the menu to move in front of the navigation bar instead of behind it. Please see my question "... drop-down box lowering _from behind the navigation bar_ but in front of my main content ..." – Lior Lavi Aug 12 '19 at 09:36
  • My bad, I missed that part! – André Ekeberg Aug 12 '19 at 12:08