Reversing default z-index for children in an HTML element
Asked Answered
C

4

9

So, the spec defines that elements are drawn "in tree order" for in-flow, non-positioned elements of similar block level or float status and identical z-index. This, of course, means those declared last in the HTML markup are drawn on top. But what if we want that order to be reversed for arbitrary children in a particular container?

For instance, say we have an indefinite number of overlapping float divs in a parent div:

__________________________________
|  _________________________      |
|  | samant| allis| rachael |...  |
|  |_______|______|_________|...  |
|_________________________________|

That we want instead to look like this:

__________________________________
|  _________________________      |
|  | samantha |lison |chael |...  |
|  |__________|______|______|...  |
|_________________________________|

jsfiddle

Is there still no way to achieve this with css alone? If not, what would be the most efficient and safe way to achieve this functionality with javascript for arbitrary child elements?

Questions have been asked previously for similiar functionality, but not specifically for use in a generic sense with an arbitrary number of child elements. See here and here.

Catarinacatarrh answered 5/1, 2015 at 21:20 Comment(3)
Here's how I'd do it in JavaScript - iterate the descendent element once to find the max z-index value and the min z-index value - then iterate them again and assign maxValue + minValue - currentValue to each one.Exemplary
Just float right, instead of left and then they're reversed.Bacolod
@slime using float:right reverses the entire list though, and aligns them to the right side. What if you want them aligned on the left and in the order in which they are declared? That's why an arbitrary solution is needed. Also, my example uses floats but the question considers block-style elements as well.Catarinacatarrh
K
5

A simple javascript solution is to get all the elements using querySelectorAll, then forEach and set the z-index to the element count - current index:

var elems = document.querySelectorAll(".container2 .floater");
Array.prototype.forEach.call(elems, function(e, i) {
    e.style.zIndex = elems.length - i;
});
.container2 {
    border: 3px solid teal;
    padding: 2em;
    display:inline-block
}


.container2 .floater {
    border: 1px solid gray;
    background: #444;
    color: white;
    float: left;
    padding: 1em;
    margin: -1em;
    position: relative;
}
<div class="container2">
    <div class="floater">Item 1</div>
    <div class="floater">Item 2</div>
    <div class="floater">Item 3</div>
    <div class="floater">Item 4</div>
    <div class="floater">Item 5</div>
    <div class="floater">Item 6</div>
</div>
Kp answered 5/1, 2015 at 21:41 Comment(4)
This will work in simple cases like my example. But, why map instead of forEach? You don't need the returned array. Also, this doesn't check for pre-set 'z-index' values, which could potentially break things.Catarinacatarrh
yes forEach is better, my mistake. In terms of pre-set z-indexes, if this is how you want it to look, why are you randomly setting z-indexes on items?Kp
I could think of cases where you may want a particular child that might be somewhere in the middle to be out in front for example. Could do something like this: var s = window.getComputedStyle(e); if(!s.zIndex || s.zIndex === 'auto'){ e.style.zIndex = elems.length - i; }Catarinacatarrh
@Kp Maybe we could set a base zIndex for the component somewhere, and then increment that based on the index of the child.Bren
H
3

You can reverse the order of the elements in the document tree to make them overlap like you want.

And then reverse their order using CSS, to place them in the right position again.

This can be achieved, for example, using

  • Flexible boxes:

    wrapper {
      display: flex;
      flex-direction: row-reverse; /* or `column-reverse` */
      justify-content: flex-end;
    }
    

    ul {
      display: flex;
      list-style: none;
      padding: 0 0 0 1em;
    }
    ul.reversed {
      flex-direction: row-reverse;
      justify-content: flex-end;
    }
    li {
      border: 1px solid;
      margin-left: -1em;
      background: #fff;
    }
    <ul>
      <li>Samantha</li>
      <li>Allison</li>
      <li>Rachael</li>
    </ul>
    <ul class="reversed">
      <li>Rachael</li>
      <li>Allison</li>
      <li>Samantha</li>
    </ul>
  • Floating elements:

    wrapper {
      float: left;
      clear: both;
    }
    item {
      float: right;
    }
    

    ul {
      list-style: none;
      float: left;
      clear: both;
      padding: 0 0 0 1em;
    }
    li {
      float: left;
      border: 1px solid;
      margin-left: -1em;
      background: #fff;
    }
    ul.reversed > li {
      float: right;
    }
    <ul>
      <li>Samantha</li>
      <li>Allison</li>
      <li>Rachael</li>
    </ul>
    <ul class="reversed">
      <li>Rachael</li>
      <li>Allison</li>
      <li>Samantha</li>
    </ul>
  • Direction:

    wrapper {
      direction: rtl;
      text-align: left;
    }
    item {
      direction: ltr;
    }
    

    ul {
      list-style: none;
      padding: 0 0 0 1em;
      text-align: left;
    }
    li {
      display: inline-block;
      border: 1px solid;
      margin-left: -1em;
      background: #fff;
    }
    ul.reversed {
      direction: rtl;
    }
    ul.reversed > li {
      direction: ltr;
    }
    <ul>
      <li>Samantha</li>
      <li>Allison</li>
      <li>Rachael</li>
    </ul>
    <ul class="reversed">
      <li>Rachael</li>
      <li>Allison</li>
      <li>Samantha</li>
    </ul>
Hotblooded answered 6/1, 2015 at 17:3 Comment(4)
Nice way to circumvent without using js, but your solutions appear to be limited to those specific element types (list items and floats). Trying to use your flex box solution to reverse standard block element z-index doesn't get the desired behavior. fiddle. Although I'm not sure why that is the case. I don't have much experience with flex boxes.Catarinacatarrh
@Catarinacatarrh It works for me (fiddle). You didn't reverse the order in the html.Hotblooded
Ah okay, I didn't realize that the second <ul> tree was reversed in your example, I thought it was all css. I do like that the solution doesn't require javascript, although declaring the html in reverse is somewhat counter-intuitive.Catarinacatarrh
@Catarinacatarrh Alternatively, JS could be used to reverse the order in the document tree: for(var i=wrapper.childNodes.length-2; i>=0; --i) wrapper.appendChild(wrapper.childNodes[i]);.Hotblooded
G
0

you could try the new flex boxes. display: inline-flex; flex-direction: column-reverse;

Goar answered 5/1, 2015 at 21:38 Comment(0)
C
0

This is not perfect, but you can put z-index: 0 to parent element and z-index: -index to your elements when the total array length is unknown.

Cytochemistry answered 5/6, 2024 at 15:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.