38

Is there any way to have a transition on the order of flex-box items?

In other words, can I have this (details in this fiddle)

#container {
    display: flex;
}
#container:hover div:last-child {
    order: -1;
}

animated (the element getting the new position assumes it's position over time), please?

Thorben Croisé
  • 12,407
  • 8
  • 39
  • 50
  • 5
    [mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties?redirectlocale=en-US&redirectslug=CSS%2FCSS_animated_properties) says that order is animatable "as an integer", [which](http://jsfiddle.net/tTHZW/1/) works in firefox. Can I have this in smooth, please? ;) – Thorben Croisé Sep 17 '13 at 09:51
  • 1
    What do you expect to happen during the transition? The element being adjusted slides over top of the other elements? – cimmanon Sep 17 '13 at 17:42
  • 1
    even though it says it can do this, i'm not sure it is going to do what you think it is. you may want to try a more javascripty approach with a library like [isotope](http://isotope.metafizzy.co/) – Jason Nov 28 '13 at 00:09
  • here's a [fiddle](http://jsfiddle.net/tTHZW/3/) of it not working like you think in FF – Jason Nov 28 '13 at 00:20
  • here is a pen of some javascript performing ordering based on the flex order that you may want to consider - https://codepen.io/osublake/pen/gaQNLK – frumbert Nov 15 '18 at 00:41
  • CSS transitions animate the thing that changed, and here it is the 'order' that changed. So when the order changes from 1 to 5, the transition will make this transition in steps 1,2,3,4,5. But this is not what you (or I) wanted. To make it visually smooth, we'll need to calculate the new position in some way, and transition the position. To do that, @neiya answer is good – Greg Woods Feb 19 '22 at 18:35

5 Answers5

33

I am not really answering the question because I am not using the order property.

But I wanted to do something similar to what you expect, and finally decided to :

  • In HTML, add a data-order attribute to the elements
  • Add the CSS properties for each element position
  • Change the data-order using Javascript
  • Using CSS transitions for the interpolation

setInterval(changeOrder, 3000);

function changeOrder() {
  const allSlides = document.querySelectorAll(".single-slide");
  const previous = "1";
  const current = "2";
  const next = "3";

  for (const slide of allSlides) {
    const order = slide.getAttribute("data-order");

    switch (order) {
      case current:
        slide.setAttribute("data-order", previous);
        break;
      case next:
        slide.setAttribute("data-order", current);
        break;
      case previous:
        slide.setAttribute("data-order", next);
        break;
    }
  }
}
.all-slides {
  display: flex;
  width: 80vw;
  margin: 0 auto;
  perspective: 500px;
  height: 500px;
}

.single-slide {
  padding: 30px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 30%;
  position: absolute;
  background-color: white;
  transition: 2s ease;
  box-shadow: 0px 5px 10px lightgrey;
}

/* Left slide*/
.single-slide[data-order="1"] {
  left: 10vw;
  transform: translate(-50%) scale(0.8, 0.8);
  z-index: 1;
  opacity: 0.7;
}

/* Middle slide */
.single-slide[data-order="2"] {
  left: 40vw;
  transform: translate(-50%);
  z-index: 3;
  opacity: 1;
}

/* Right slide*/
.single-slide[data-order="3"] {
  left: 90vw;
  transform: translate(-120%) scale(0.8, 0.8);
  z-index: 2;
  opacity: 0.7;
}

.single-slide:nth-child(2) {
  order: 3;
}

.single-slide:nth-child(1) {
  order: 2;
}

.single-slide:nth-child(3) {
  order: 1;
}
<div class="all-slides">
  <div class="single-slide" data-order="2">
    <h3>First slide </h3>
    <p>Some text</p>
  </div>
  <div class="single-slide" data-order="3">
    <h3>Second slide</h3>
    <p>Some other text</p>
  </div>
  <div class="single-slide" data-order="1">
    <h3>Third slide</h3>
    <p>Yet some other text</p>
  </div>
</div>

This could be useful if you want to animate a slider (or anything else), but want to keep the order of the elements in the HTML for accessibility purposes, which is one of the useful usage of the order property. See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Ordering_Flex_Items#The_order_property_and_accessibility

neiya
  • 2,657
  • 4
  • 23
  • 32
26

Sadly no: the order attribute is animatable, but only as integers. That means that for each step/frame of the animation it will interpolate the value by flooring to the neareast integer. So items will only ever show up in the slot that the computed integer value results in, never in-between in any smooth sort of motion way.

It's technically still an animation: the calculated integer position should still follow the timing function and keyframe rules of the animation, it's just that the items "jump" from position to position as they change.

See https://developer.mozilla.org/en-US/docs/Web/CSS/integer#Interpolation

Emil
  • 1,949
  • 2
  • 16
  • 25
6

This question is old now, but I recently tested this, using this Fiddle (adapted from the one posted by Jason in a comment): http://jsfiddle.net/aqrxcd1u/ (code below).

In both Chrome and Firefox this partially animates, in that the order transitions one at a time from the current value to the target value. Meaning it doesn't go from 5->1 but instead goes 5->4->3->2->1.

In desktop Safari it still goes 5->1 directly.

#container {
    display: flex;
}
#container div {
    width: 100px;
    height: 100px;
    margin-right: 10px;
    background-color: red;    
}
#container div:nth-child(even) {
    background-color: blue;
}
}
#container div:last-child {
    order: 5;
    transition: order 1s;
}
#container:hover div:last-child {
    order: -1 !important;
}
<div id="container">
    <div style="order: 1">Element 1A</div>
    <div style="order: 2">Element 2B</div>
    <div style="order: 3">Element 3C</div>
    <div style="order: 4">Element 4D</div>
    <div style="order: 5">Element 5E</div>
</div>
Tom Anthony
  • 791
  • 7
  • 14
  • but the transition isn't smooth at all, it actually jumps. But i guess that makes sense, since there's no interpolation between order. – windmaomao Aug 21 '22 at 01:21
3

So here is what I created based on the above:

// Some simple logic that will "change the order of elements"
function shuffleThem() {
  var items = [...document.getElementsByClassName('item')];
  items.sort(() => Math.random() < 0.5 ? -1 : 1);
  items.forEach((item, index) => {
    item.style.setProperty('--order', index);
  })
}
.container {
  position: relative;
}
.placeholder {
  /*
  A .placeholder will push the .container's height
  while its .item is absolutely positioned inside.
  */
  background: red;
  width: 10rem;
  height: 3rem;
}
.item {
  /*
  All items are initially rendered at the top of the .container
  and are later transformed/translated to their ordered positions.
  */
  position: absolute;
  top: 0;
  width: 10rem;
  height: 3rem;
  
  transition: transform 0.5s ease;
  transform: translateY(calc(var(--order) * 100%));
    
  text-align: center;
  line-height: 3rem;
}
<button onclick="shuffleThem()">Shufle </button>

<br /><hr /><br />

<p>Content before the .container</p>

<div class="container">
  <div class="placeholder"></div>
  <div class="placeholder"></div>
  <div class="placeholder"></div>
  <div class="placeholder"></div>
  <div class="placeholder"></div>
  
  <div class="item" style="--order: 0; background: #FFBE66">A</div>
  <div class="item" style="--order: 1; background: #66FF9F">B</div>
  <div class="item" style="--order: 2; background: #FFFFFF">C</div>
  <div class="item" style="--order: 3; background: #FF8166">D</div>
  <div class="item" style="--order: 4; background: #70C1FF">E</div>
</div>

<p>Content after the .container</p>

Not saying it fits all cases but it did suit mine.

  • One major requirement for this approach is that you know (or determine) the heights of your items so that you can have placeholders for them.
  • A major flaw in the approach is that you cannot use the wrapping abilities of flexbox. You are either reordering vertically (as in the example) or horizontally.

Here is the same thing as a CodePen.

Valery
  • 715
  • 6
  • 19
2

As Emil stated, it only animates as an integer. However, I am thinking of a hack:

  • Put the element you want to display in a wrapper with 1/10 of the height, set the wrapper overflow: visible
  • Then put 9 spacing element between these wrappers with the same height.
  • Put order and transition on all of them.
  • Change order of a wrapper and watch it 'transitioning'.

Sadly, it's ugly and only work in Firefox. Here is what I tested in Angular

Jules
  • 1,677
  • 1
  • 19
  • 25