8

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.

Community
  • 1
  • 1
NanoWizard
  • 2,104
  • 1
  • 21
  • 34
  • 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. – Benjamin Gruenbaum Jan 05 '15 at 21:23
  • Just float right, instead of left and then they're reversed. – Waxi Jan 05 '15 at 21:24
  • @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 `float`s but the question considers block-style elements as well. – NanoWizard Jan 05 '15 at 21:27

3 Answers3

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>
NanoWizard
  • 2,104
  • 1
  • 21
  • 34
Rhumborl
  • 16,349
  • 4
  • 39
  • 45
  • 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. – NanoWizard Jan 06 '15 at 16:20
  • 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? – Rhumborl Jan 06 '15 at 16:23
  • 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; }` – NanoWizard Jan 06 '15 at 16:33
  • @Rhumborl Maybe we could set a base zIndex for the component somewhere, and then increment that based on the index of the child. – sayandcode Oct 07 '22 at 05:29
2

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>
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • 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](http://jsfiddle.net/jvsdmo72/). Although I'm not sure why that is the case. I don't have much experience with flex boxes. – NanoWizard Jan 08 '15 at 16:00
  • @NanoWizard It works for me ([fiddle](http://jsfiddle.net/jvsdmo72/1/)). You didn't reverse the order in the html. – Oriol Jan 08 '15 at 22:31
  • Ah okay, I didn't realize that the second `
      ` 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.
    – NanoWizard Jan 09 '15 at 14:45
  • @NanoWizard 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]);`. – Oriol Jan 09 '15 at 15:33
0

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

potis
  • 13
  • 4