2

I'm looking for some help with the most efficient and performant way to generate this loop.

| COL 1 | COL 2 | COL 3 |
|   1   |   2   |   3   |
|   4   |   5   |   6   |
|   7   |   8   |   9   |
|   10  |   11  |   12  |

Columns here are unordered lists and I want to ordered by postDate going across each column. It needs to be done this way as specific to the design of the page.

I've tried batch, slice , loop indexes and experimented with range. Tried 3 different craft.entries loops to generate the columns.

What I would really need if it existed was offset and nth if nth didn't terminate.

I'm guessing answer will be something like creating these arrays outside of final entries loop and passing in IDs?

2

I tried a simple solution using javascript, you can translate the code into twig looping. I hope it helps.

const posts = [1,2,3,4,5,6,7,8,9,10,11,12];
const colNum = 3;
const rowNum = posts.length/colNum;

for (i=0 ; i<rowNum ; i++) {
 const row = [];
 for (j=0; j<colNum; j++) {
   row.push(posts[(i*colNum) + j]);
 }
 console.log(row)
 console.log('----')
}

output :

enter image description here


improve solution using twig

@JohnHenryDonovan ah sorry, I didn't notice the batch filter. Why don't you sort the entries first, then batch them?

I assume you use bootstrap as CSS lib, then you can apply d-flex on each row.

{% set projects = craft.entries()
      .section('project')
      .orderBy('postDate desc')
      .all() %}

{% set grouped = projects|batch(3) %}
{% for row in grouped %}
   <div class="d-flex">
   {% for column in row %}
      <div class="item-{{loop.index}}">
         <!-- render your item here -->
      </div>
   {% endfor %}
   </div>
{% endfor %}

The output of the code above

<div class="d-flex">
  <div class="item-1"> ... </div>
  <div class="item-2"> ... </div>
  <div class="item-3"> ... </div>
</div>
<div class="d-flex">
  <div class="item-1"> ... </div>
  <div class="item-2"> ... </div>
  <div class="item-3"> ... </div>
</div>
<div class="d-flex">
  <div class="item-1"> ... </div>
  <div class="item-2"> ... </div>
  <div class="item-3"> ... </div>
</div>

jsfiddle HTML output http://jsfiddle.net/30k9ndg6/3/

4
  • Wouldn't this just be the same as batch? {% set columns = projects|batch(3) %} I need to figure out how to get the following essentially using your JS as an example const posts = [1,4,7,10,2,5,8,11,3,6,9,12]; – John Henry Donovan May 13 '19 at 8:33
  • I think Im gonna go Javascript route in this. It will keep my entry list clean and allow me to adjust columns in mobile. Similar to jsfiddle.net/UrVsR/1 – John Henry Donovan May 13 '19 at 9:31
  • @JohnHenryDonovan see my updated answer above – Dzulfikar Adi Putra May 13 '19 at 10:30
  • Thanks but that wont work. See my original diagram That will just flow entries into 3 columns like col1 - 1,2,3,4 col2 - 5,6,7,8, col3 - 9,10,11,12. I have it working now with JS though – John Henry Donovan May 13 '19 at 15:08
1

Decided on Javascript for my solution on this one rather than twig. Reasons being that my entries still held their order in the markup and I can adjust to 2 columns if needs be for responsive breakpoint

{% set entries = craft.entries.section('projects').all() %}


<div id="columns">
    {% for entry in entries %}
    <li>
        <h2>{{ entry.title }}</h2>
    </li>
    {% endfor %}
</div>


<script>

$(function(){
    var first = $('<ul/>').addClass('box').addClass('first-column';
    var mid = $('<ul/>').addClass('box').addClass('second-column');
    var last = $('<ul/>').addClass('box').addClass('third-column');

    var fElems = $('#columns li:nth-child(3n+1)');
    var mElems = $('#columns li:nth-child(3n+2)');
    var lElems = $('#columns li:nth-child(3n+3)');

    fElems.appendTo(first);
    mElems.appendTo(mid);
    lElems.appendTo(last);

    $('#columns').append(first,mid,last);
});
</script>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.