Skip to content Skip to sidebar Skip to footer

Flexbox: Reverse Direction On Wrap / "snake Wrap"

I would like to find a way to have elements in a single container wrap to a new line going the opposite direction of the previous line, like a snake curving back on itself. I have

Solution 1:

A CSS grid solution similar to this one that works with a fixed number of elements per row (aka fixed number of columns).

.flex-container {
  width: 500px;
  background: orange;
  display: grid;
  grid-template-columns:repeat(5,1fr); /*define the number of column*/grid-auto-flow:dense; /* this is important to fill all the space*/grid-gap:20px;
  padding:10px;
}

.item {
  background: purple;
  height: 80px;
  line-height: 80px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}

.item:nth-child(10n + 6)  {grid-column:5}
.item:nth-child(10n + 7)  {grid-column:4}
.item:nth-child(10n + 8)  {grid-column:3}
.item:nth-child(10n + 9)  {grid-column:2}
/*.item:nth-child(10n + 10) {grid-column:1} not needed*//* For N = number of columns 
  .item:nth-child((2xN)n + (N+1)) { grid-column:N; }
  .item:nth-child((2xN)n + (N+2)) { grid-column:(N-1); }
  ....
  .item:nth-child((2xN)n + (2xN)) { grid-column:1; }

*/.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 40014px'Arial', sans-serif;
  counter-reset:num;
}
<divclass="flex-container"><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div></div>

If you want a generic solution here is one using JS and flexbox. The trick is to set the order property based on the row position of each item. I will rely on this old answer to do most of the calculation in order to find the number of rows/columns:

//total number of elementvar elems = document.querySelectorAll('.item');
var n_t = elems.length;
//width of an elementvar w = parseInt(document.querySelector('.item').offsetWidth);
//full width of element with marginvar m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
//width of containervar w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
//padding of containervar c = document.querySelector('.flex-container').currentStyle || window.getComputedStyle(document.querySelector('.flex-container'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);


var adjust = function(){
   //only the width of container will change
   w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
   //Number of columns
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
   //Number of rows
   nc = Math.ceil(n_t/nb);
   for(var j = 0;j<nb;j++) {
     for(var i = 0;i<nc;i++) {
       if(j + i*nb >= n_t) /* we exit if we reach the number of elements*/breakif(i%2!=1) 
         elems[j + i*nb].style.order=j + i*nb; /* normal flow */else
         elems[j + i*nb].style.order=(nb - j) + i*nb; /* opposite flow */
     }
    }
}

adjust()
window.addEventListener('resize', function(){adjust()})
.flex-container {
  background: orange;
  display: flex;
  flex-wrap:wrap;
}

.item {
  background: purple;
  height: 80px;
  width:80px;
  margin:10px;
  line-height: 80px;
  grid-gap:20px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}


.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 40014px'Arial', sans-serif;
  counter-reset:num;
}
<divclass="flex-container"><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div></div>

There is a little issue with the last line in some cases when it's not full of item. You can rectify this by adding some margin to the last element

//total number of elementvar elems = document.querySelectorAll('.item');
var n_t = elems.length;
//width of an elementvar w = parseInt(document.querySelector('.item').offsetWidth);
//full width of element with marginvar m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
//width of containervar w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
//padding of containervar c = document.querySelector('.flex-container').currentStyle || window.getComputedStyle(document.querySelector('.flex-container'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);


var adjust = function(){
   //only the width of container will change
   w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
   //Number of columns
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
   //Number of rows
   nc = Math.ceil(n_t/nb);
   for(var j = 0;j<nb;j++) {
     for(var i = 0;i<nc;i++) {
       if(j + i*nb >= n_t) /* we exit if we reach the number of elements*/break
       elems[j + i*nb].style.marginLeft='10px'; /*we rest the margin*/if(i%2!=1) 
         elems[j + i*nb].style.order=j + i*nb; /* normal flow */else {
         elems[j + i*nb].style.order=(nb - j) + i*nb; /* opposite flow *//*margin fix*/if(i == (nc - 1) && (j + i*nb == (n_t - 1)) && j<(nb-1)) {
            elems[j + i*nb].style.marginLeft=((nb*nc - n_t)*w + 10) + 'px';
         }
       }
     }
    }
}

adjust()
window.addEventListener('resize', function(){adjust()})
.flex-container {
  background: orange;
  display: flex;
  flex-wrap:wrap;
}

.item {
  background: purple;
  height: 80px;
  width:80px;
  margin:10px;
  line-height: 80px;
  grid-gap:20px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}


.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 40014px'Arial', sans-serif;
  counter-reset:num;
}
<divclass="flex-container"><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div></div>

You can easily switch to the other direction by simply changing if(i%2!=1) to if(i%2!=0)

//total number of elementvar elems = document.querySelectorAll('.item');
var n_t = elems.length;
//width of an elementvar w = parseInt(document.querySelector('.item').offsetWidth);
//full width of element with marginvar m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
//width of containervar w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
//padding of containervar c = document.querySelector('.flex-container').currentStyle || window.getComputedStyle(document.querySelector('.flex-container'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);


var adjust = function(){
   //only the width of container will change
   w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
   //Number of columns
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
   //Number of rows
   nc = Math.ceil(n_t/nb);
   for(var j = 0;j<nb;j++) {
     for(var i = 0;i<nc;i++) {
       if(j + i*nb >= n_t) /* we exit if we reach the number of elements*/break
       elems[j + i*nb].style.marginLeft='10px'; /*we rest the margin*/if(i%2!=0) 
         elems[j + i*nb].style.order=j + i*nb; /* normal flow */else {
         elems[j + i*nb].style.order=(nb - j) + i*nb; /* opposite flow *//*margin fix*/if(i == (nc - 1) && (j + i*nb == (n_t - 1)) && j<(nb-1)) {
            elems[j + i*nb].style.marginLeft=((nb*nc - n_t)*w + 10) + 'px';
         }
       }
     }
    }
}

adjust()
window.addEventListener('resize', function(){adjust()})
.flex-container {
  background: orange;
  display: flex;
  flex-wrap:wrap;
}

.item {
  background: purple;
  height: 80px;
  width:80px;
  margin:10px;
  line-height: 80px;
  grid-gap:20px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}


.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 40014px'Arial', sans-serif;
  counter-reset:num;
}
<divclass="flex-container"><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div><divclass="item"></div></div>

Post a Comment for "Flexbox: Reverse Direction On Wrap / "snake Wrap""