1

I am trying to make my entry loop have a load more button, and not sure how to do it. I tried the method from http://craftsnippets.com/articles/infinite-scrolling-and-lazy-loading-with-craft-cms but I could not get it to work.

Below is the code for my entry loop and "Load More" button. What would be the easiest way to make the "Load More" button functional?

            <div class="more-posts-wrap">

                {% set moreABetterPost = craft.entries.section('aBetterPost').all() %}
                {% for entry in moreABetterPost %}
                <a href="{{ entry.getUrl }}" class="more-post">
                    <div class="more-post-img-wrap">
                        {% set featuredImage = entry.featuredImage.one() %}
                        <img src="{{ featuredImage.getUrl }}" class="more-post-img">
                        <div class="red-overlay"></div>
                    </div>
                    <div class="more-post-content">
                        <div class="more-post-title">
                            <h3>{{ entry.title }}</h3>
                        </div>
                        <div class="next-icon">
                            <i class="fas fa-chevron-right"></i>
                        </div>
                    </div>
                </a>
                {% endfor %}

            </div>

            <div class="load-button-wrap">
                <a href="#">
                    <div class="load-button">
                        <p>Load More</p>
                    </div>
                </a>
            </div>

EDIT: Clarifying the structure of my site.

{# _main.twig #}
Header Stuff
Header Stuff
...
{% block content %}
{# All of my pages exist in here by extending _main.twig #}
Page Stuff
Page Stuff
Page Stuff
...
LOAD MORE ENTRIES AREA
{% endblock %}
Footer Stuff
Footer Stuff
Footer Stuff
...
{# End of _main.twig #}

EDIT 2: Showing the code that's not working in regards to James Smith's Answer.

_loadMore.twig

{% set theLayout = craft.app.request.isAjax and not craft.app.request.isLivePreview ? '_ajaxLayout' : '_main' %}
{% extends theLayout %}

<section>
    <div class="more-posts-section">
        <div class="section-header">
            <h2>More Posts in <span>{{ category.title }}</span></h2>
        </div>

        {% set entries = craft.entries.section('aBetterPost').limit(6) %}
        {% paginate entries as pageInfo, pageEntries %}


        {% block content %}
        <div class="more-posts-wrap-border">
            <div class="more-posts-wrap js-ajaxPagination">
                {% for entry in aBetterPost%}
                  {% include '_layouts/_post' %}
                {% endfor %}
            </div>
        </div>
        {% endblock %}
        
        <div class="load-button-wrap">
            <a href="{{ '/' ~ craft.app.request.pathInfo ~ '/p' ~ (pageInfo.currentPage + 1) }}" class="load-button js-loadMore">{{'load more'|t}}</a>
        </div>

        {% block ajaxContent %}
            {% for entry in aBetterPost%}
              {% include '_layouts/_post' %}
            {% endfor %}
        {% endblock %}
    </div>
</section>

{# ======================================
AJAX PAGINATION SCRIPT
========================================= #}

{% if pageInfo is defined and pageInfo.total > 6 %}
    {% js %}
        var currentPage = 1;
        var perPage = 6;
        var count = perPage;
        $('.js-loadMore').click(function(e){
            e.preventDefault();
            if({{ pageInfo.total }} > count){
                $.ajax({
                    url: $(this).attr('href'),
                }).done(function(data) {
                    var $new = $('.js-ajaxPagination').append(data);
                    var $button = $('.js-loadMore');
                    // increment the counters
                    count = count + perPage;
                    currentPage++;
                    $button.show().attr('href', '/{{ craft.app.request.pathInfo }}/p' + (currentPage + 1));
                    if({{ pageInfo.total }} <= count){
                        $button.remove();
                    }
                });
            }
        });
    {% endjs %}
{% endif %}

_page.twig

{% extends "_layouts/_main" %}
{% block content %}
   {# Other Page Stuff#}
   {# Other Page Stuff#}
   {% include '_layouts/_loadMore' %}
{% endblock %}
  • 1
    Have you set up element api endpoint as described in article and added js code to website that handle ajax requests? Also, alternative to using solution described in my article, would be using sprig plugin: putyourlightson.com/sprig-cookbook#load-more – piotrpog Jul 7 at 1:04
  • Why bother with the added complexity of using the Element API or adding and learning yet another abstraction like Sprig? Just call up your same listing template and have two Twig blocks - one for Ajax requests and one for regular requests. Keep it simple! – James Smith Jul 7 at 9:22
  • @piotrpog I added the JS, but I think the API stuff is where I got hung up. That Sprig plugin might be a good option, but I'm always a bit leery about non-first-party plugins with keeping up with Craft updates and such. – Justin Kendall Jul 7 at 12:19
  • @JamesSmith I am definitely not married to the method that I tried. If there is an easier/better way to do it, I'm all ears! Could you explain the process you mentioned a little more? – Justin Kendall Jul 7 at 12:21
3

Here's a cut-down version of what I'm using on https://cld.agency/showcase

It's using jQuery because that's the way I like it:

{% set theLayout = craft.app.request.isAjax and not craft.app.request.isLivePreview ? '_ajaxLayout' : '_layout' %}
{% extends theLayout %}

{% set entries = craft.entries.section('news').limit(12) %}
{% paginate entries as pageInfo, pageEntries %}

{# =============================================================================== #}

{% block content %}     
    <h1>News</h1>
    <p>Introduction</p>
    <ul class="js-ajaxPagination">      
        {% for card in pageEntries %}
            <li>{% include '_partials/card'  %}</li>
        {% endfor %}
    </ul>
    {# allow no-js/googlebot fallback with a regular href to the next page... #}
    <p><a class="js-loadMore" href="{{ '/' ~ craft.app.request.pathInfo ~ '/p' ~ (pageInfo.currentPage + 1) }}">Load more</a></p>
{% endblock %}

{# =============================================================================== #}

{% block ajaxContent %}
    {% for card in pageEntries %}
        <li>{% include '_partials/card' %}</li>
    {% endfor %}
{% endblock %}

{# ======================================
AJAX PAGINATION SCRIPT
========================================= #}

{% if pageInfo is defined and pageInfo.total > 12 %}
    {% js %}
        var currentPage = 1;
        var perPage = 12;
        var count = perPage;
        $('.js-loadMore').click(function(e){
            e.preventDefault();
            if({{ pageInfo.total }} > count){
                $.ajax({
                    url: $(this).attr('href'),
                }).done(function(data) {
                    var $new = $('.js-ajaxPagination').append(data);
                    var $button = $('.js-loadMore');
                    // increment the counters
                    count = count + perPage;
                    currentPage++;
                    $button.show().attr('href', '/{{ craft.app.request.pathInfo }}/p' + (currentPage + 1));
                    if({{ pageInfo.total }} <= count){
                        $button.remove();
                    }
                });
            }
        });
    {% endjs %}
{% endif %}

So the trick is forking which template gets extended depending on whether it's an ajax request. Your _ajaxLayout template would be as simple as just:

{{ block('ajaxContent') }}

| improve this answer | |
  • Thanks for the answer, looks great on your site! I'm having a bit of trouble getting it to work on my end. Would it be possible to see where my code would plug into this system? (Sorry, still learning :) ) – Justin Kendall Jul 7 at 13:23
  • It's impossible to say without knowing your exact setup, and there are lots of ways of achieving the same result... My example assumes that you have a global layout template called _layout and that that template contains a {{ endBody() }} tag to accept the injected js and a content block to output the content. – James Smith Jul 7 at 13:35
  • Ok, I'm running into some snags. I don't think I have the structure for this set up correctly. My site is currently set up with my header/footer in a file called _main then all of my pages extends _main via {% block content%}. I'm currently trying to do this "More Posts" area in its own file _loadMore which I then include in my page. Right now it is doing some weird stuff like having multiple buttons and messing up the layout. And when I click the button, it literally duplicates the entire page in the area. So, I'm a bit confused on how you're using theLayout with your ajax – Justin Kendall Jul 9 at 13:29
  • Ok, I just realized that the funky layout stuff, was an error on my part. But it is still duplicating the page when I click my "Load More" button – Justin Kendall Jul 9 at 13:39
  • What is in your ajaxLayout template? (or whatever you named it) – James Smith Jul 9 at 13:51

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.