1

I need to rearrange my flex items in their container programmatically, but I can't seem to find how to select them by their order numbers. The problem is that they are not shown in their DOM order, but using order css attribute.

How do I rearrange them?

my html:

<div id="container">
    <div style="order: 17"></div>
    <div style="order: 25"></div>
    <div style="order: 2"></div>
    <div style="order: 3"></div>
    // ...
</div>

Basically I need to do this:

| A | B | C | D | E |
       +----------->
     <---+---+---+
result:
| A | C | D | E | B |
Qwerty
  • 29,062
  • 22
  • 108
  • 136

2 Answers2

2

So, I got an idea to abuse CSS3 selectors a bit and it seems to be working, though I wonder how legit this is.

document.querySelector('div[style*="order: 25;"]')

Then to swap two items I need to do this:

var t = document.querySelector('div[style*="order: 25;"]');
document.querySelector('div[style*="order: 26;"]').style.order = 25;
t.style.order = 26;
Qwerty
  • 29,062
  • 22
  • 108
  • 136
  • 2
    This will work perfectly provided every flex item is guaranteed to have the style attribute with its own ordinal number and the style declaration itself is written and/or serialized consistently. Unfortunately, [CSS3 `attr()` isn't implemented anywhere](http://stackoverflow.com/questions/8769786/css3s-attr-doesnt-work-in-major-browsers/8769922#8769922), otherwise you would have been able to make this much cleaner by replacing the inline style with a data attribute and using something like `#container > div { order: attr(data-order integer); }`. – BoltClock Jun 17 '15 at 02:50
1

You can sort the flex items according to their computed order.

Note that, in case two flex items have the same order, they should be ordered according to their DOM position. That is, the sort must be stable. Since [].sort is not necessarily stable, I will use a custom implementation of merge sort.

var orderedChildren = mergeSort([].map.call(
  document.getElementById('container').children,
  function(child) {
    return [child, +getComputedStyle(child).order];
  }
)).map(function(pair) {
  return pair[0];
});
function mergeSort(arr) {
  var m = Math.floor(arr.length / 2);
  if(!m) return arr;
  var arr2 = mergeSort(arr.splice(m)),
      arr1 = mergeSort(arr.splice(0));
  while(arr1.length && arr2.length)
    arr.push((arr1[0][1] > arr2[0][1] ? arr2 : arr1).shift());
  return arr.concat(arr1, arr2);
}

var orderedChildren = mergeSort([].map.call(
  document.getElementById('container').children,
  function(child) {
    return [child, +getComputedStyle(child).order];
  }
)).map(function(pair) {
  return pair[0];
});
function mergeSort(arr) {
  var m = Math.floor(arr.length / 2);
  if(!m) return arr;
  var arr2 = mergeSort(arr.splice(m)),
      arr1 = mergeSort(arr.splice(0));
  while(arr1.length && arr2.length)
    arr.push((arr1[0][1] > arr2[0][1] ? arr2 : arr1).shift());
  return arr.concat(arr1, arr2);
}
for(var i=0; i<orderedChildren.length; ++i) {
  orderedChildren[i].textContent = i;
}
#container {
  display: flex;
  flex-wrap: wrap;
}
#container > div {
  border: 1px solid;
  padding: 5px;
}
<div id="container">
  <div style="order: 17"></div>
  <div style="order: 25"></div>
  <div style="order: 2"></div>
  <div style="order: 3"></div>
  <div style="order: 17"></div>
  <div style=""></div>
  <div style="order: -17"></div>
  <div style="order: 2"></div>
</div>
If the numbers are ordered, it means JS has iterated the flex items according to their <code>order</code> property.
Oriol
  • 274,082
  • 63
  • 437
  • 513