How to create in a for loop list unequal rows (2,4,3,2)

Viewed 85

I'm really hoping that you can help me. I have a for loop

  <ul>
  {% for product in collection.products  %}
     <li>{{product}}</li>
 {% endfor %}
 </ul>

I would like to create unequal rows: first row - 2 products second: 4 third 3 fourth: 2 fifth: 4 ..... And have something like this:

I'm trying to do this 2 month already. Could you please help me?

enter image description here

3 Answers

So you want rows like

00 01
02 03 04 05
06 07 08 

You can use that schema to create css classes like this

.col0, .col1{
  width: 50%;
}
.col2, .col3, .col4, .col5{
  width: 25%;
}
.col6, .col7, .col8{
  width: 33.3%;
}

And then in your code:

{% for product in collection.products %}
    <li class="col{{ forloop.index0 | modulo : 9}}">{{product.title}}</li> 
{% endfor %}

Note that this will work for an arbitrary number of elements. The final html will look like this

<ul>
<li class="col0">x</li>
<li class="col1">x</li>
<li class="col2">x</li>
<li class="col3">x</li>
<li class="col4">x</li>
<li class="col5">x</li>
<li class="col6">x</li>
<li class="col7">x</li>
<li class="col8">x</li>
<li class="col0">x</li>
<li class="col1">x</li>
<li class="col2">x</li>
...
</ul>

This is an example of the result.

.col0, .col1{
  min-width: 50%;
}
.col2, .col3, .col4, .col5{
  min-width: 25%;
}
.col6, .col7, .col8{
  min-width: 33.3%;
}
ul{
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
}
li{
  display: block;
  flex: 1;
}


li, just-for-better-display{
  background-color: #FAB100;
  border: 1px solid #0FAB10;
  margin: 5px -1px;
  padding: 5px 0;
  text-align: center;
}
<ul>
<li class="col0">1</li>
<li class="col1">2</li>
<li class="col2">3</li>
<li class="col3">4</li>
<li class="col4">5</li>
<li class="col5">6</li>
<li class="col6">7</li>
<li class="col7">8</li>
<li class="col8">9</li>
<li class="col0">10</li>
<li class="col1">11</li>
<li class="col2">12</li>
<li class="col2">13</li>
<li class="col2">14</li>
</ul>

Note that I've switched to min-width (and added flex: 1) to stretch the last elements to the maximum possible width.

Not sure if you can do it with plain liquid itself without any tricks, but using js and css you will have a bit more freedom:

<ul id="collection-grid">
  {% for product in collection.products  %}
    <li>{{product}}</li>
  {% endfor %}
</ul>

<style>
  ul#collection-grid {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
  }
  
  ul#collection-grid>li {
    display: block;
    background-color: yellow;
    margin: -1px;
    border: 1px solid black;
    text-align: center;
  }
  
  ul#collection-grid>li.col-6 {
    width: 50%
  }
  
  ul#collection-grid>li.col-3 {
    width: 25%
  }
  
  ul#collection-grid>li.col-4 {
    width: 33.3333%
  }

</style>


<script>
  document.addEventListener("DOMContentLoaded", function(event) {
    const chunkConfig = [{
      items: 2,
      className: "col-6"
    }, {
      items: 4,
      className: "col-3"
    }, {
      items: 3,
      className: "col-4"
    }];
    const listItems = document.querySelectorAll("#collection-grid > li");
    for (let i = 0, chunkIndex = 0, chunkItemsCount = 0; i < listItems.length; i++) {
      const chunk = chunkConfig[chunkIndex];

      listItems[i].classList.add(chunk.className);

      if (++chunkItemsCount === chunk.items) {
        chunkItemsCount = 0;
        if (chunkIndex === chunkConfig.length - 1) chunkIndex = 0;
        else chunkIndex++;
      }
    }
  });

</script>

Just modify css a little to make it fit your needs.

UPD: I wrote this fiddle for tests purposes https://jsfiddle.net/4y8d1kvq/

  • Main logic I have used is I have filtered the index like keeping only index of selected two and skip next three and stored it in filteredProductIndexes. and than check weather it contains an index or not with filteredProductIndexes.includes(index).
  • filteredProductIndexes.includes(index) this will retunr true if index is there and false if not.
  • if above condition satisfied than I have added class .setHalf, which has css of flex:1 0 calc(50% - 30px);(it means two items before wraping).
  • and all the other product I have set css to flex:1 0 calc(33.33% - 30px); (this means three items before wrapping).
  • Talking about skipFilter(seq, pick, skip) pick is how many item we want to select and skip is how many items we want to skip and seq is an array of indexes.
  • I have found this function from this stackoverflow answer please visit that link for more information.
  • I am unsure why stackoverflow console is not loading but you can run this code in your project I have tested it on codepen and it works fine. Here is codepen screenshot
function skipFilter(seq, pick, skip) {
  var stride = pick + skip;

  return seq.filter(function(_, i) {
    return i % stride < pick;
  });
}

const products = document.querySelectorAll(".product-container .product");
let productIndexes = [];
let pick = 2,
  skip = 3;
products.forEach((product, index) => {
  productIndexes.push(index);
})
filteredProductIndexes =
  skipFilter(productIndexes, pick, skip);

function skipFilter(seq, pick, skip) {
  var stride = pick + skip;

  return seq.filter(function(_, i) {
    return i % stride < pick;
  });
}
products.forEach((product, index) => {
  if (filteredProductIndexes.includes(index)) {
    product.classList.add("setHalf");
  }
})
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

ul li {
  list-style-type: none;
}

.container {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
}

.product-container {
  display: flex;
  justify-content: space-between;
  gap: 15px;
  flex-wrap: wrap;
}

.product-container .product {
  flex: 1 0 calc(33.33% - 30px);
}

.product-container .product.setHalf {
  flex: 1 0 calc(50% - 30px);
}

.product-container .product img {
  height: 300px;
  width: 100%;
  object-fit: cover;
}
<div class="container">
  <ul class="product-container">
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
    <li class="product">
      <img src="https://source.unsplash.com/random">
    </li>
  </ul>
</div>

Related