Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weighted or relative ordering of flexbox items

Tags:

css

flexbox

As I understand, the flexbox css property order is absolute, i.e. the last element in a flex container with order: 0 raises to the top.

I'd like to be able to promote certain elements in a flex container so they won't raise to the exact place but instead by a number of elements, e.g. move the element above the previous one.

To clarify:

<div>A</div>
<div>B</div>
<div>C</div>
<div style="order: -1">This should go up by one, between B and C</div>

That style will of course move the last element to the top and not where I want it to be.

Is there a way to achieve this with just css?


Apparently I'm asking the impossible. Since I'll likely be needing this kind of ordering at some point, I'll just have to make an ordering system in javascript that changes the children's order property on the fly. I have it kind of figured out, but not clearly enough to put in words right now.

I guess it's worth mentioning that this is for a layout editor that will be exporting plain html and css. Javascript will only be used in the editor and not the published site. Otherwise it would be enough to just reorder the actual elements, but at some points certain elements need to be prioritized for different devices.

I'll eventually answer this question with the method I come up with but in the meantime feel free to add your own suggestions and grab the points for accepted answer :)

like image 649
jpeltoniemi Avatar asked Sep 04 '14 11:09

jpeltoniemi


1 Answers

Here's a CSS-only solution, using CSS's var() and calc().

Codepen

Step 1: Give all items an explicit index

When we render the list, we make sure to explicitly set an index per item in the list (otherwise they default to '0' if none set). We use a CSS var for that because it's more easily accessible within CSS calc; otherwise we'd may want to use attr() which is more of an option if you do JS logic).

<div id="wrapper">
  <!-- Each item has an explicit CSS var defined -->
  <div style="--data-myIndex:0;">A</div>
  <div style="--data-myIndex:1;">B</div>
  <div style="--data-myIndex:2;">C</div>
  <div style="--data-myIndex:3;">D</div>
  <div style="--data-myIndex:4;">E</div>
  <div style="--data-myIndex:5;">F</div>
  <div style="--data-myIndex:6;">G</div>
</div>

<style>
#wrapper > * {
  margin: 1em;
  padding: 1em;
  background-color: #EEEEEE;
  
  /* We set the order based on the CSS var myIndex (set in HTML) */
  order: var(--data-myIndex);
}
</style>

Step 2: Shift an item relatively

With all items having an explicit index, we can then shift a specific item in a relative way (e.g. +/- x) using our index value within calc().

<style>
@media(max-width: 768px) {
  
     /* Shift a specific element (e.g. 3rd) by some number (e.g. 2); we use the index inside calc to do a relative shift! */
    #wrapper div:nth-child(3) {
      order: calc(var(--data-myIndex) + 2);
      background-color: #AAAAAA;
    }
}
</style>

In this use case, I'm doing it at a breakpoint. In mobile view, we shift a particular item by a relative amount. It's only possible because everything has an explicit index.

You could also do this shift in JS if you prefer. If using JS, you could use a data attribute on elements as opposed to CSS var.

like image 99
MarsAndBack Avatar answered Sep 30 '22 22:09

MarsAndBack