34

I am trying to understand the rules behind z-index and how it interacts with the overflow property.

I have this html:

<body>
  <div class="cell">
    Here is some text to keep things interesting
    <div class="boxy"></div>
  </div>
</body>

And this css:

.boxy {
  position: absolute;
  z-index: 9999;
  top:70px;
  width: 50px;
  height: 50px;
  background: #0FF;
}

.cell {
  border: 2px solid #F00;
  position: relative;

  /* comment these two lines out and the box appears */
  /* or change them both to 'visible' */
  /* changing only one of them to 'visible' does not work */
  overflow-y: auto;
  overflow-x: auto;
}

I would have expected that the cyan box appears even though it is out of the size of the div.cell because its z-index and its position are set.

However, the only way to make the cyan box appear is to comment out the overflow-x and -y lines.

My question is: How can I make the cyan box appear on the screen while keeping the overflow as either hidden or auto? But more importantly, I'm looking to understand why this is happening. What are the css and layout rules being applied here?

See my Plunkr. This example, is of course a much simplified version of the HTML/CSS I am actually working with.


EDIT There seems to be some confusion in the answers below because I didn't explain things well enough. If you comment the two overflow lines out, you can see that the cyan box appears. It appears outside of the border of .cell. Why does this happen? How can I make the cyan box appear, while still hiding overflow and z-index?

Andrew Eisenberg
  • 28,387
  • 9
  • 92
  • 148

5 Answers5

19

The reason the cyan box appears only when overflow-x and overflow-y are visible, and disappears otherwise, is simply because the cyan box is overflowing the cell box. overflow: visible simply means "paint this box even if it is overflowing its containing block" — the cell box is the containing block of the cyan box because its position is relative. Any other values of overflow cause overflowing content to be clipped from view. There is nothing special going on here; in particular, the z-index is completely irrelevant and there is no such interaction as the question title alludes to (and there really is no reason to set it to such a huge number unless you're worried about scripts injecting other elements into the cell).

The only way to allow the cyan box to appear while the cell has a non-visible overflow is to remove position: relative from the cell and apply that declaration to the parent of the cell (in your example, it's the body). Like this:

body {
  position: relative;
}

.boxy {
  position: absolute;
  z-index: 9999;
  top: 70px;
  width: 50px;
  height: 50px;
  background: #0FF;
}

.cell {
  border: 2px solid #F00;
  overflow: auto;
}
<div class="cell">
  Here is some text to keep things interesting
  <div class="boxy"></div>
</div>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • OK. I think I get it now. `overflow` controls what happens to DOM stuff outside of the visible boundaries of the element. `z-index` has to do with stacking of visible elements. So, if an element is not visible, it cannot be stacked. Thanks for the clarification. ps- in the actual application, I use saner values for z-index. 9999 was only to prove (to myself) that the problem was not about a z-index that was too low. – Andrew Eisenberg Jun 20 '16 at 14:17
  • @Andrew Eisenberg: The element can still stack - you just don't see it when overflow is not visible. It's why layout isn't affected by scrolling. – BoltClock Jun 20 '16 at 14:18
  • Unfortunately, this means that my approach will not work since it relies on `.boxy` having a position relative to `.cell`. By placing the `position: relative` higher up the DOM, I need to use JS to calculate proper positioning of each `.boxy` element. – Andrew Eisenberg Jun 20 '16 at 14:19
  • @AndrewEisenberg Hi Andrew, I've encountered the same issue you had. Just wondering how you solved it. – WoShiNiBaBa Jan 15 '17 at 21:15
  • In the end, I had to rearchitect this part of the dom. I had been trying to implement popups with locally defined dom elements. The change was to create a single popup dom element dangling off of the root that displays, hides, and moves based mouse interactions with the local dom elements. I'm not 100% pleased with the solution because scrolls are janky, but it is better than before. – Andrew Eisenberg Jan 16 '17 at 00:00
4

Absolute-positioned elements do not contribute to the dimensions of their parents.

Therefore, the .cell DIV has no content that affects its dimensions, making it 100% wide by 0px high.

To make the element appear, you'll have to add a height to .cell that will encompass the DIV, in this case 120px (70px top + 50px height).

Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
  • I understand the part about the .cell having no dimensions. I made a slight change to the question so that the parent div has text in it. But I do not want to make the `.cell` have a height. I am adding the element to the z-index since I want it to float above the other elements. My goal here is to create a sticky tooltip that moves along with the element it points to. – Andrew Eisenberg Jun 20 '16 at 03:53
  • Ahh, I understand your question better now. Interesting. – Rick Hitchcock Jun 20 '16 at 03:55
  • Interesting, indeed. :) I just hope there's a silly little thing I am missing. – Andrew Eisenberg Jun 20 '16 at 03:58
2

The Parent Class cell need to be set it's height. because height of absolute element doesn't affect it;s parent.

 .boxy {
      position: absolute;
      z-index: 9999;
      top:70px;
      width: 50px;
      height: 50px;
      background: #0FF;

    }

    .cell {
      border: 2px solid #F00;
      position: relative;

      /* comment these two lines out and the box appears */
      /* or change them both to 'visible' */
      /* changing only one of them to 'visible' does not work */
      overflow-y: auto;
      overflow-x: auto;
      min-height: 120px; /* height 70px(Top)+50px*/
    }
Gautam Jha
  • 1,445
  • 1
  • 15
  • 24
  • 1
    That's not what I want. I don't want `.cell` to change its height based on `.boxy`. You can see that if you remove the two `overflow` lines, the box appears, and it is outside the location of `.cell`. – Andrew Eisenberg Jun 20 '16 at 03:57
1

Your problem

Your problem is related to cell node that hides boxy when overflow is specified on cell node.

The reason

The reason behind is that boxy with position absolute does not contribute to height of cell and overflow hides it.

Why is it shown without overflow?

By default overflow is visible, which for browser means do not do anything special for overflow functionality and it does not need to render overflow => does not hide boxy.

gevorg
  • 4,835
  • 4
  • 35
  • 52
  • Adding the height and width make the `.cell` larger, but that's not what I want. I want `.boxy` to float above all other elements, outside of the `.cell`. – Andrew Eisenberg Jun 20 '16 at 03:55
  • @AndrewEisenberg then just use `relative` instead of `absolute`, look updated version. – gevorg Jun 20 '16 at 04:01
  • Not really. That only works when you move the box into the coordinates of the cell itself. I want the box to be visible even when it is outside the boundaries of `.cell`. This happens when you comment out overflow. – Andrew Eisenberg Jun 20 '16 at 04:04
  • @AndrewEisenberg for that case you have to write some javascript, because `.boxy` cannot be outside without absolute positioning and cannot contribute to height with it. Relatively good option would be to have absolutely position `.boxy` and set `height` of `.cell` with javascript when it is inside of it. – gevorg Jun 20 '16 at 04:08
  • Why does this work when there is no overflow set? I don't need to set the height of `.cell` in that case. – Andrew Eisenberg Jun 20 '16 at 04:12
  • @AndrewEisenberg I guess it is, because by default `overflow` is `visible`, and for browser that means **do not do anything special for `overflow`**, but when you specify `overflow: auto` it detects `cell`'s `height` and renders overflow in a way that hides `boxy`. – gevorg Jun 20 '16 at 04:23
1

Z-indices are local inside their clipping hierarchical parent context. This is very non-intuitive. They have their own z-stack context, which normally parallels that of the enclosure hierarchy. But they're still subject to clipping! Which can be a real pain if you're intuitively expecting the z-indices to be absolute.

Note that some jquery containers, such as accordion, quietly specify overflow: auto. Even if it's not explicitly in your code. (This can be overridden locally after it's found.)

Also note that if overflow-x: visible is set, but overflow-y is set to a non-visible, then the rendering engine quietly internally changes overflow-x to be the same as overflow-y for your amusement. But you found this out already.

You probably should be able to circumvent the unwanted non-"visible" overflow clipping, even with your high z-index, by invoking transform: translate(0,0); [or whatever desired offset, % or pixels] inside the style of the div that you want to levitate. Transform should create a new local z-stack for that element and its children. Which will let you get around an overly-restrictive parent or grandparent.

DragonLord
  • 6,395
  • 4
  • 36
  • 38