Isotope Layout with Pagination? Or incremental loading at least?

I am helping a client with a WordPress template that is using Isotope.js in a masonry layout.

Isotope.min.js: http://isotope.metafizzy.co/ or source code here: http://isotope.metafizzy.co/jquery.isotope.js

Read More

The base template: http://themetrust.com/demos/ink/

The problem is that they have hundreds and hundreds of items that will need to be in that portfolio so displaying them becomes an issue. Obviously we don’t want all of them to load at once. Even with some form of lazyloading integrated, it would still be a massive size.

QUESTION: Would it be possible to alter Isotope to include some kind of pagination? Maybe a “click here to load more” div along the very bottom that then loads the next batch of items? Or even better – something in the main loop so that every 20th block just says “Load More Items” and clicking it replaces those first 19 items with the next 19 items (19 items + the Load More block so that there is always a grid of 20)?

Basically, I’m just trying to figure out the best way to still use Isotope (so we don’t have to completely redo sections of the template the client likes) but have it incrementally load it’s items so that load times and page size are adversely affected. Any help would be greatly appreciated!

Related posts

Leave a Reply

1 comment

  1. I faced the same issue and finally managed to add pagination to isotope. Though my solution works, it is not helpful when managing large data, since all data is loaded and consequently filtered.

    First, I divided all my content into pages and assigned matching css classes (page1, page2, page3, …) to each item. Then I added pagination to the frontend and I set the matching filter with JavaScript. Here are some code examples from my TYPO3 Extbase setup:

    PHP in Controller to calculate css classes for page filters:

    $itemsPerPage = 6;
    $c = 1;
    $catPages = array(); // needed for categories, so that both filters (page and category) work together
    foreach ( $projects AS $key => $project ) {
        $page = ceil( $c / $itemsPerPage );
        $projects[ $key ]->pageClass = 'page' . $page;
        $c++;
    
        // Get all categories of project to calculate the category-related page
        $cats = $project->getCategories();
        foreach ( $cats AS $cat ) {
            /* @var $cat TYPO3CMSExtbaseDomainModelCategory */
            $catid = $cat->getUid();
            $catPages[ $catid ]++;
            $catPage = ceil( $catPages[ $catid ] / $itemsPerPage );
            $projects[ $key ]->pageClass .= ' cat' . $catid . 'page' . $catPage;
        }
    }
    $nrPages = ceil( count( $projekts ) / $itemsPerPage );
    
    $nrCatPages = array();
    foreach ( $catPages AS $key => $value ) {
        $object = new stdClass();
        $object->catid = $key;
        $object->nrPages = ceil( $value / $itemsPerPage );
        $nrCatPages[] = $object;
    }
    
    $this->view->assign( 'projects', $projects );
    $this->view->assign( 'kategorien', $kategorien );
    $this->view->assign( 'nrPages', $nrPages );
    $this->view->assign( 'nrCatPages', $nrCatPages );
    

    With this code, I had all the needed css classes calculated.

    HTML in template

    In the HTML code I assigned the CSS class to each item, which has been calculated in the controller by using <div class="item {project.pageClass}">...

    Then I added the HTML-Code for the pagination:

    <div id="pagination-wrap">
    
        <!-- Pagination for all items (no category selected) -->
        <f:if condition="{nrPages}>1">
            <ul class="pagination-cm all">
                <li class="pageinfo">Page <span class="currentPage">1</span> of {nrPages}</li>
                <li class="prev"><a class="page-selector inactive" data-page="prev">&lt;</a></li>
                <v:iterator.for from="1" to="{nrPages}" iteration="i">
                    <f:if condition="{i.isFirst}">
                        <f:then><li><a class="page-selector inactive" data-page-nr="{i.index}" data-page="page{i.index}">{i.index}</a></li></f:then>
                        <f:else><li><a class="page-selector" data-page-nr="{i.index}" data-page="page{i.index}">{i.index}</a></li></f:else>
                    </f:if>
                </v:iterator.for>
                <li class="next"><a class="page-selector" data-page="next">&gt;</a></li>
            </ul>
        </f:if>
    
        <!-- Paginations for each category -->
        <f:if condition="{nrCatPages}">
            <f:for each="{nrCatPages}" as="catpages">
                <f:if condition="{catpages.nrPages}>1">
                    <ul class="pagination-cm category cat{catpages.catid}">
                        <li class="pageinfo">Page <span class="currentPage">1</span> of {catpages.nrPages}</li>
                        <li class="prev"><a class="page-selector inactive" data-page="prev">&lt;</a></li>
                        <v:iterator.for from="1" to="{catpages.nrPages}" iteration="i">
                            <f:if condition="{i.isFirst}">
                                <f:then><li><a class="page-selector inactive" data-page-nr="{i.index}" data-page="cat{catpages.catid}page{i.index}">{i.index}</a></li></f:then>
                                <f:else><li><a class="page-selector" data-page-nr="{i.index}" data-page="cat{catpages.catid}page{i.index}">{i.index}</a></li></f:else>
                            </f:if>
                        </v:iterator.for>
                        <li class="next"><a class="page-selector" data-page="next">&gt;</a></li>
                    </ul>
                </f:if>
            </f:for>
        </f:if>
    
    </div>
    

    JavaScript

    Finally, I just needed the following JavaScript, to make the pagination functionable with isotope (all JS, no reloading of the page necessary!):

    jQuery(document).ready(function () {
        /* Isotope Pagination */
        var $container = jQuery('.portfolio');
        // start by showing page 1 on page load
        $container.isotope({
            filter: '.page1'
        });
    
        /* when changing category, display correct pagination */
        jQuery('.port-filter li a').click(function () {
            jQuery('.pagination-cm').hide();
            var pagination = jQuery(this).parent().attr("data-pagination");
            jQuery('.pagination-cm.' + pagination).find("li.pageinfo > span.currentPage").text(1);
            jQuery('.pagination-cm.' + pagination).show();
            refreshPagination();
        });
    
        /* when pagination item is clicked, use isotope filter to show correct items */
        jQuery('.portfolio-container').on('click', '.page-selector:not(.inactive)', function () {
            var $pagination = jQuery(this).closest("ul");
            var $currentPage = $pagination.find("li.pageinfo > span.currentPage");
            var page = jQuery(this).attr("data-page");
            var current = parseInt($currentPage.text());
    
            var newPage = 1;
            if (page == 'next') {
                newPage = current + 1;
                page = (($pagination.find("li:not(.next):not(.prev) .page-selector").attr("data-page")).slice(0, -1) + newPage);
            }
            else if (page == 'prev') {
                newPage = current - 1;
                page = (($pagination.find("li:not(.next):not(.prev) .page-selector").attr("data-page")).slice(0, -1) + newPage);
            }
            else {
                newPage = jQuery(this).text();
            }
    
            $currentPage.text(newPage);
            $container.isotope({
                filter: '.' + page,
                animationOptions: {
                    duration: 750,
                    easing: 'linear',
                    queue: false
                },
                layoutMode: 'fitRows'
            });
    
            refreshPagination();
    
            return false;
        });
    
        /* refresh pagination display: disable current page button and prev/next buttons if necessary */
        function refreshPagination() {
            jQuery('.portfolio-container .pagination-cm:visible').each(function (index, value) {
                var $pagination = jQuery(value);
                var currentPage = parseInt($pagination.find("li.pageinfo > span.currentPage").text());
                $pagination.find("a.page-selector").removeClass("inactive");
    
                // Aktuelle Seite
                $pagination.find("li a.page-selector[data-page-nr=" + currentPage + "]").addClass("inactive");
    
                // Prev-Button
                if (currentPage == 1) $pagination.find("li.prev a.page-selector").addClass("inactive");
                else $pagination.find("li.prev a.page-selector").removeClass("inactive");
    
                // Next-Button
                var lastPage = $pagination.find("li:not(.next):last .page-selector").attr("data-page-nr");
                if (currentPage < lastPage) $pagination.find("li.next a.page-selector").removeClass("inactive");
                else $pagination.find("li.next a.page-selector").addClass("inactive");
            });
        }
    
    });
    

    Like I said this comes from a TYPO3 context. I implemented this code in a custom extension based on extbase. These code fragments won’t be portable to other projects without adjustments, but I hope they will still be helpful.