CSS going-around content flow

  • A+
Category:Languages

Is there a way to define a content flow in CSS using flex or other techniques such that content "zig zags" or goes around in this way:

 ----------------- |A > B > C > D > E| |J < I < H < G < F|  -----------------   --- |A H| |B G| |C F| |D E|  --- 

Assume there are always 2 columns or rows. I could split the items in 2 and create 2 wrapping items around them, but I would like it to be more dynamic.

Basically, how do I make the first row flow to the right and the second flow to the left?

 


It's clear that a pure scalable CSS solution doesn't actually exist in order to achieve such thing so you will need some scripting to dynamically adjust some properties in order to obtain the needed layout.

If we suppose that all the elements have the same width we can identify the number of element per row and apply styles to the elements depending on the row.

Here is a basic example based on a code of this previous answer: https://stackoverflow.com/a/49046973/8620333

//total number of element var n_t = $('.item').length; //full width of element with margin var w = $('.item').outerWidth(true); //width of container without padding var w_c = $('.grid').width(); //nb element per row var nb = Math.min(parseInt(w_c / w),n_t); console.log(nb); $('.item:nth-child(1n+'+(nb+1)+')').addClass('right'); $('.item:nth-child(1n+'+(2*nb+1)+')').removeClass('right'); $('.item:nth-child(1n+'+(3*nb+1)+')').addClass('right'); $('.item:nth-child(1n+'+(4*nb+1)+')').removeClass('right');  window.addEventListener('resize', function(event){    //only the width of container will change    w_c = $('.grid').width();    nb = Math.min(parseInt(w_c / w),n_t);    $('.item').removeClass('right');      $('.item:nth-child(1n+'+(nb+1)+')').addClass('right');   $('.item:nth-child(1n+'+(2*nb+1)+')').removeClass('right');   $('.item:nth-child(1n+'+(3*nb+1)+')').addClass('right');   $('.item:nth-child(1n+'+(4*nb+1)+')').removeClass('right'); });
.grid {   background-color: #ddd;   padding: 10px 0 0 10px;   overflow:hidden; }  .item {   width: 80px;   height: 80px;   float:left;   clear:right;   background-color: red;   margin: 0 10px 10px 0; } .item.right {   float:right;   clear:left;   background:blue; }  body {   margin:0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="grid">   <div class="item">A</div>   <div class="item">B</div>   <div class="item">C</div>   <div class="item">D</div>   <div class="item">E</div>   <div class="item">F</div>   <div class="item">G</div>   <div class="item">H</div>   <div class="item">I</div>   <div class="item">J</div>   <div class="item">K</div>   <div class="item">L</div>   <div class="item">M</div>   <div class="item">N</div>   <div class="item">O</div>   <div class="item">P</div> </div>

This example isn't a perfect one as we have issue with alignment but the idea is to apply floating property to rows by alternating rows. I only considered 4 rows but we can easily make it dynamic using a loop like below:

//total number of element var n_t = $('.item').length; //full width of element with margin var w = $('.item').outerWidth(true); //width of container without padding var w_c = $('.grid').width(); //nb element per row var nb = Math.min(parseInt(w_c / w),n_t); for(var i=1;i<n_t;i++) { if(i%2==1)    $('.item:nth-child(1n+'+(i*nb+1)+')').addClass('right'); else    $('.item:nth-child(1n+'+(i*nb+1)+')').removeClass('right'); }  window.addEventListener('resize', function(event){    //only the width of container will change    w_c = $('.grid').width();    nb = Math.min(parseInt(w_c / w),n_t);    $('.item').removeClass('right');   for(var i=1;i<n_t;i++) {     if(i%2==1)       $('.item:nth-child(1n+'+(i*nb+1)+')').addClass('right');     else       $('.item:nth-child(1n+'+(i*nb+1)+')').removeClass('right');   } });
.grid {   background-color: #ddd;   padding: 10px 0 0 10px;   overflow:hidden; }  .item {   width: 80px;   height: 80px;   float:left;   clear:right;   background-color: red;   margin: 0 10px 10px 0; } .item.right {   float:right;   clear:left;   background:blue; }  body {   margin:0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="grid">   <div class="item">A</div>   <div class="item">B</div>   <div class="item">C</div>   <div class="item">D</div>   <div class="item">E</div>   <div class="item">F</div>   <div class="item">G</div>   <div class="item">H</div>   <div class="item">I</div>   <div class="item">J</div>   <div class="item">K</div>   <div class="item">L</div>   <div class="item">M</div>   <div class="item">N</div>   <div class="item">O</div>   <div class="item">P</div> </div>

To have better alignment, we can consider the use of CSS grid or flexbox but in this case we will need to adjust the order property of the elements.

With CSS grid:

//total number of element var n_t = $('.item').length; //full width of element with margin var w = $('.item').outerWidth(true); //width of container without padding var w_c = $('.grid').width(); //nb element per row var nb = Math.min(parseInt(w_c / w), n_t); //nb rows var nr = n_t / nb; //order of element var or = 0;  for (var i = 0; i < nr; i++) {   if (i % 2 == 0)     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + j).css('order', or++);     }   else     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);     } }  window.addEventListener('resize', function(event) {   //only the width of container will change   w_c = $('.grid').width();   nb = Math.min(parseInt(w_c / w), n_t);   nr = n_t / nb;   or = 0;   for (var i = 0; i < nr; i++) {     if (i % 2 == 0)       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + j).css('order', or++);       }     else       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);       }   } });
.grid {   background-color: #ddd;   display: grid;   grid-template-columns: repeat( auto-fit, 80px);   padding: 10px 0 0 10px; }  .item {   height: 80px;   background-color: red;   font-size: 30px;   color: #fff;   font-weight: bold;   margin: 0 10px 10px 0; }  body {   margin: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="grid">   <div class="item">A</div>   <div class="item">B</div>   <div class="item">C</div>   <div class="item">D</div>   <div class="item">E</div>   <div class="item">F</div>   <div class="item">G</div>   <div class="item">H</div>   <div class="item">I</div>   <div class="item">J</div>   <div class="item">K</div>   <div class="item">L</div>   <div class="item">M</div>   <div class="item">N</div>   <div class="item">O</div>   <div class="item">P</div> </div>

This method allow a better alignment but the last row isn't always good.

We can correct the last row by adjusting the last elements usgin grid-column like below:

//total number of element var n_t = $('.item').length; //full width of element with margin var w = $('.item').outerWidth(true); //width of container without padding var w_c = $('.grid').width(); //nb element per row var nb = Math.min(parseInt(w_c / w), n_t); //nb rows var nr = Math.ceil(n_t / nb); //order of element var or = 0;  for (var i = 0; i < nr; i++) {   if (i % 2 == 0)     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + j).css('order', or++);     }   else     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);       /*fix the last row*/       if (i == (nr - 1)) {         $('.item').eq(nb * i + j).css('grid-column', " " + (nb - j));       }     } }  window.addEventListener('resize', function(event) {   //only the width of container will change   w_c = $('.grid').width();   nb = Math.min(parseInt(w_c / w), n_t);   nr = Math.ceil(n_t / nb);   $('.item').css('grid-column', 'auto');   or = 0;   for (var i = 0; i < nr; i++) {     if (i % 2 == 0)       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + j).css('order', or++);       }     else       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);         /*fix the last row*/         if (i == nr - 1) {           $('.item').eq(nb * i + j).css('grid-column', " " + (nb - j));         }       }   } });
.grid {   background-color: #ddd;   display: grid;   grid-template-columns: repeat( auto-fit, 80px);   grid-auto-flow: dense;   padding: 10px 0 0 10px; }  .item {   height: 80px;   background-color: red;   font-size: 30px;   color: #fff;   font-weight: bold;   margin: 0 10px 10px 0; }  body {   margin: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="grid">   <div class="item">A</div>   <div class="item">B</div>   <div class="item">C</div>   <div class="item">D</div>   <div class="item">E</div>   <div class="item">F</div>   <div class="item">G</div>   <div class="item">H</div>   <div class="item">I</div>   <div class="item">J</div>   <div class="item">K</div>   <div class="item">L</div>   <div class="item">M</div>   <div class="item">N</div>   <div class="item">O</div>   <div class="item">P</div> </div>


Flexbox can be more suitable for the second case (the column direction). We simply do the same thing as previously considering columns instead of rows:

//total number of element var n_t = $('.item').length; //full height of element with margin var w = $('.item').outerHeight(true); //height of container without padding var w_c = $('.grid').height(); //nb element per row var nb = Math.min(parseInt(w_c / w), n_t); //nb rows var nr = n_t / nb; //order of element var or = 0;  for (var i = 0; i < nr; i++) {   if (i % 2 == 0)     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + j).css('order', or++);     }   else     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);     } }  window.addEventListener('resize', function(event) {   //only the width of container will change   w_c = $('.grid').height();   nb = Math.min(parseInt(w_c / w), n_t);   nr = n_t / nb;   or = 0;   for (var i = 0; i < nr; i++) {     if (i % 2 == 0)       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + j).css('order', or++);       }     else       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);       }   } });
.grid {   display: flex;   height:100vh;   flex-direction:column;   flex-wrap:wrap;   align-items:flex-start;   align-content:flex-start;   padding-top: 10px;   padding-left:10px;   box-sizing:border-box; }  .item {   height: 80px;   width:80px;   background-color: red;   font-size: 30px;   color: #fff;   font-weight: bold;   margin: 0 10px 10px 0; }  body {   margin: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="grid">   <div class="item">A</div>   <div class="item">B</div>   <div class="item">C</div>   <div class="item">D</div>   <div class="item">E</div>   <div class="item">F</div>   <div class="item">G</div>   <div class="item">H</div>   <div class="item">I</div>   <div class="item">J</div>   <div class="item">K</div>   <div class="item">L</div>   <div class="item">M</div>   <div class="item">N</div>   <div class="item">O</div>   <div class="item">P</div> </div>

There is also an alignment issue in some cases with the last column that we can fix by adjust margin:

//total number of element var n_t = $('.item').length; //full height of element with margin var w = $('.item').outerHeight(true); //height of container without padding var w_c = $('.grid').height(); //nb element per row var nb = Math.min(parseInt(w_c / w), n_t); //nb rows var nr = Math.ceil(n_t / nb); //order of element var or = 0; for (var i = 0; i < nr; i++) {   if (i % 2 == 0)     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + j).css('order', or++);     }   else {     for (var j = 0; j < nb; j++) {       $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);     }     if (i == (nr - 1)) {       /*we add margin+height of non-existing element*/       $('.item:last').css('margin-top', ((nb * nr - n_t) * (80 + 10)) + "px")     }   } }  window.addEventListener('resize', function(event) {   //only the width of container will change   w_c = $('.grid').height();   nb = Math.min(parseInt(w_c / w), n_t);   nr = Math.ceil(n_t / nb);   or = 0;   $('.item').css('margin-top', 0); /*reset the margin*/   for (var i = 0; i < nr; i++) {     if (i % 2 == 0)       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + j).css('order', or++);       }     else {       for (var j = 0; j < nb; j++) {         $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);       }       if (i == (nr - 1)) {         /*we add margin+height of non-existing element*/         $('.item:last').css('margin-top', ((nb * nr - n_t) * (80 + 10)) + "px")       }     }   } });
.grid {   display: flex;   height: 100vh;   flex-direction: column;   flex-wrap: wrap;   align-items: flex-start;   align-content: flex-start;   padding-top: 10px;   padding-left: 10px;   box-sizing: border-box; }  .item {   height: 80px;   width: 80px;   background-color: red;   font-size: 30px;   color: #fff;   font-weight: bold;   margin: 0 10px 10px 0; }  body {   margin: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="grid">   <div class="item">A</div>   <div class="item">B</div>   <div class="item">C</div>   <div class="item">D</div>   <div class="item">E</div>   <div class="item">F</div>   <div class="item">G</div>   <div class="item">H</div>   <div class="item">I</div>   <div class="item">J</div>   <div class="item">K</div>   <div class="item">L</div>   <div class="item">M</div>   <div class="item">N</div>   <div class="item">O</div>   <div class="item">P</div> </div>

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: