2

I followed this tutorial here on how to paginate without reloading the page: http://craftsnippets.com/articles/dynamic-pagination-for-craft-cms

it uses Ajax to load in pagination without re-loading or exiting the page.

(it seems to be the most up to date tutorial I could find, but please correct me if I am wrong).

I have this working for one entry (a group of simple fields):

Before pagination:

enter image description here

After pagination:

enter image description here

So all is going well - the problem is when I need to show more than one of these on the page, I can get the pagination to load, but when I click the next ajax button (to load more content) - it either: Loads all the content together (loads the next page of all three) - or it loads the next page but updates all the content (the three lists) to have the same content.

Some examples:

Each list is added - with there own pagingation - but clicking next updates each list - not just the list that was clicked:

enter image description here

Image below shows that all paginated elements have moved to the next page instead of just the top one that was clicked on...

enter image description here

So - What I think is happening is that Ajax is being called for all elements and not just the speicic one that is being clicked on: cards on the table I have spent about 8 dev hours trying to figure this out and could really do with a keen eye that knows more about Ajax than me to point out the mistake:

Here is my code:

Index.twig

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="\ASSETS\CSS\main.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>

<body>

    <header>

    </header>

    <body>
        <section class="grid-col-3 container">
            {% set tableData = entry.tableData.all() %}
            {% for table in tableData %}
                <div class="table">
                    <h3>{{table.alpha}}</h3>
                    <p>{{table.bravo}}</p>
                    <p>{{table.charlie|date('m/d/Y')}}</p>
                </div>
            {% endfor %}
        </section>

        {% include 'IMPORT/pagination/dynamic_pagination' with {
            pagination_list: 'IMPORT/pagination/pagination_pages_list'
        } %}


    </body>

    <!-- <footer>

    </footer> -->
    
</body>

dynamic_pagination.twig

        {# v2 #}
    {% if pagination_list is defined %}
    {% js %}
    
    // AJAX REQUEST DATA
    {% set current_url = craft.request.getRequestUri()|split(craft.request.getPath())[0]~craft.request.getPath() %}
    {% set ajax_data = {
        current_url: current_url,
        pagination_list: pagination_list|hash,
        pagination_parameters: pagination_parameters ?? null,
    } %}
    
    
    // JS SETTINGS
    var loading_class = 'is-loading'
    var pagination_list_class = 'js-pages-list'
    var pagination_wrapper_class = 'js-pages-wrapper'
    var animation_speed = 1000
    
    // TWIG TO JS
    var endpoint_url = '{{url(pagination_endpoint ?? 'pagination_endpoint')}}'
    var url_params = '{{craft.app.request.getQueryStringWithoutPath() is not empty ? '?' ~ craft.app.request.getQueryStringWithoutPath()}}';
    var current_url = '{{current_url}}'
    var page_trigger = '{{craft.app.config.general.pageTrigger}}'
    
    // AJAX REQUEST
    var current_request = null;  
    function change_page(page_number, done){
    
        current_request = $.ajax({
          url: endpoint_url+'/'+page_trigger+page_number+url_params,
          method: 'GET',
          data: {{ajax_data|json_encode|raw}},
          beforeSend: function(){
            if(current_request != null) {
                current_request.abort();
            }
            $('.'+pagination_list_class).addClass(loading_class);
    
            var page = $("html, body");
    
            page.on("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove", function(){
               page.stop();
            });
    
            page.animate({ scrollTop: 
                $('.'+pagination_wrapper_class).position().top }, animation_speed, function(){
                page.off("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove");
            });
    
          }
        }).always(function(){
            $('.'+pagination_list_class).removeClass(loading_class);
        }).done(function(data) {
            $('.'+pagination_wrapper_class).html(data);
            if(done){
                done();
            }
        });
    }
    
    // BACK/FORWARD BUTTON
    var initial_page = '{{craft.request.getPageNum()}}'
    window.addEventListener('popstate', function(e) {
        if(e.state){
            change_page(e.state);
        }else{
            change_page(initial_page);
        }
    });
    
    // PAGINATION CLICK EVENT
    $('.'+pagination_wrapper_class).on('click', '[data-number]', function(e){
        e.preventDefault();
        var selected_page = $(this).attr('data-number');
        change_page(selected_page, function(){
            history.pushState(selected_page, null, current_url+'/'+page_trigger+selected_page+url_params);
        });
    });
    
    {% endjs %}    
    
    <div class="js-pages-wrapper container">
        {% include pagination_list with {
            pagination_parameters: pagination_parameters ?? null,
        } %}
    </div>
    
    {% endif %}


pagination_pages_list.twig

{# v1 #}
{# example element query - adjust it to your website #}
{% set tableData = craft.entries.section('home').one().tableData %}
{% set tableData2 = craft.entries.section('home').one().tableData2 %}
{% set tableData3 = craft.entries.section('home').one().tableData3 %}


{% paginate tableData.limit(3) as pageInfo, pageEntries %}
{% paginate tableData2.limit(3) as pageInfo2, pageEntries2 %}
{% paginate tableData3.limit(3) as pageInfo3, pageEntries3 %}


{% if craft.request.isAjax() %}
    {% do pageInfo.setBasePath(craft.request.getParam('current_url')) %}
{% endif %}

{# replace code below with your entries list and pagination component #}
{% include 'IMPORT/components/pagination' %}
<br>
<div class="js-pages-list grid-col-3">
{% for entry in pageEntries %}
    <div class="entryItem">
        <h3>{{entry.alpha}}</h3>
        <p>{{entry.bravo}}</p>
        <p>{{entry.charlie|date('d,m,Y')}}</p>
    </div>
{% endfor %}
</div>

{% include 'IMPORT/components/pagination2' %}
<br>
<div class="js-pages-list grid-col-3">
{% for entry in pageEntries2 %}
    <div class="entryItem">
        <h3>{{entry.alpha}}</h3>
        <p>{{entry.bravo}}</p>
        <p>{{entry.charlie|date('d,m,Y')}}</p>
    </div>
{% endfor %}
</div>

{% include 'IMPORT/components/pagination3' %}
<br>
<div class="js-pages-list grid-col-3">
{% for entry in pageEntries3 %}
    <div class="entryItem">
        <h3>{{entry.alpha}}</h3>
        <p>{{entry.bravo}}</p>
        <p>{{entry.charlie|date('d,m,Y')}}</p>
    </div>
{% endfor %}
</div>

pagination.twig <<<

I have duplicated this file for each of the lists and changed the just changed the pageInfo to either pageInfo2 or pageInfo3

{# requires pageInfo variable #}

{# settings #}
{% set neighbours = 1 %}

{# symbols #}
{% set prev = '&#10094;' %}
{% set next = '&#10095;' %}
{% set dots = "&hellip;" %}

{# single numeric link #}
{% macro numericLink(url, number, current) %}
<li>
    <a href="{{url}}" data-number="{{number}}" class="pagination-link {{current ? 'is-current' : null}}" aria-label="{{current ? 'current page'|t : 'go to page'|t ~ ' ' ~ number}}" {{current ? 'aria-current="page"'}}>{{number}}</a>
</li>
{% endmacro %}

{# next/prev link #}
{% macro textLink(url, content, aria, number) %}
<li>
    <a href="{{url}}" data-number="{{number}}" class="pagination-link" aria-label="{{aria}}">{{content|raw}}</a>
</li>
{% endmacro %}

{# ellipsis #}
{% macro ellipsis(content) %}
<li>
    <span class="pagination-ellipsis">{{content|raw}}</span>
</li>
{% endmacro %}

{# pagination logic #}
{% if pageInfo is defined and pageInfo.totalPages > 1 %}
{% import _self as self %}

{# seomatic #}
{# https://github.com/nystudio107/craft-seomatic#pagination-and-seo #}
{% if seomatic is defined %}
    {% do seomatic.helper.paginate(pageInfo) %}
{% endif %}


<nav class="pagination" role="navigation" aria-label="{{'pagination'|t}}">
<ul class="pagination-list">

    {# previous #}
    {% if pageInfo.prevUrl %}
        {{ self.textLink(pageInfo.prevUrl, prev, 'previous page'|t, pageInfo.currentPage - 1) }}
    {% endif %}

    {# first #}
    {% if pageInfo.currentPage - neighbours > 1  %}
        {{ self.numericLink(pageInfo.firstUrl, '1') }}
    {% endif %}

    {# ellipsis before current #}
    {% if pageInfo.currentPage - neighbours > 6 %}
        {{ self.ellipsis(dots) }}
    {% endif %}

    {# links before current #}
    {% for page, url in pageInfo.getPrevUrls(neighbours) %}
        {{ self.numericLink(url, page) }}
    {% endfor %}

    {# current #}
    {{ self.numericLink('', pageInfo.currentPage, true) }}

    {# links after current #}
    {% for page, url in pageInfo.getNextUrls(neighbours) %}
        {{ self.numericLink(url, page) }}
    {% endfor %}

    {# ellipsis after current #}
    {% if pageInfo.totalPages - pageInfo.currentPage > neighbours + 1 %}
        {{ self.ellipsis(dots) }}
    {% endif %}

    {# last #}
    {% if pageInfo.currentPage + neighbours < pageInfo.totalPages %}
        {{ self.numericLink(pageInfo.lastUrl, pageInfo.totalPages) }}
    {% endif %}

    {# next #}
    {% if pageInfo.nextUrl %}
        {{ self.textLink(pageInfo.nextUrl, next, 'next page'|t, pageInfo.currentPage + 1) }}
    {% endif %}

</ul>
</nav>
{% endif %}

You can find the full git gist of these files on this link (from the tutorial not my modifed version)

https://gist.github.com/piotrpog/6c8d76a882fef8c82ea8eda99317bf1d

--

I know this is pretty hacky - so if there is any other way that you would do this to make it simpler then please let me know and I would happy to go that route.

Thanks in advance - W

1

I suggest to use Sprig plugin provide all ajax and other filter related functionality https://putyourlightson.com/plugins/sprig check it out.

use your pagination class and get url after that call ajax.

$(document).on('click', '.pagination-link', function(event) {
        event.preventDefault();

        var url = $(this).attr('href');

        $('.loading').show();
        $.ajax({
            url: url,
            type: 'GET',
            dataType: 'html',
            cache: true,
        })
        .done(function(data, status, xhr) {
            console.log('AJAX Paginate finished. Showing Results to product container')
            $('.feed').html(data).show();

        })
        .fail(function() {
            console.log("AJAX Paginate Failed. Please review the query and feed template");
        })
        .always(function() {
            $('.loading').hide();
            console.log("Hiding Loading bar and Closing pagination call.");
        });
    });

Hope it helps...

2
  • Hi thanks for the plugin suggestion - I will check that out - Is the second part of your answer based on using that plugin or is it a fix to the code that I have posted ahead? If so could I please ask you to post it with the surrounding code as I am having a hard time seeing where to implement it. Thanks for your help - very appreciated
    – Wally
    Jun 29 at 12:46
  • 1
    The second answer for your solution. you have to create a js file or inline js. i used your pagination class to grab the url and do the work.
    – Taher
    Jun 30 at 8:07

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.