Laravel Infinite Scroll

Laravel Infinite Scroll
Photo by Jona Troes / Unsplash

I needed to quickly implement a very simple infinite scroll behaviour on a very simple page. Due laziness I got a look for any dirty hack suggested by random people on the internet to do that in Laravel, ready to be copied and pasted; I found a method using jScroll jQuery plugin, copied and pasted it, and it didn't work. But I understood the essential trick, and reimplemented it by myself.

It is based on native pagination function in Laravel, and uses the automatically generated paginator to obtain the links for the next page to load at each iteration.

Controller sample code:

<?php

namespace App\Http\Controllers;

use App\Something;

class SampleController extends Controller
{
    public function index()
    {
        $nodes = Something::orderBy('id', 'asc')->paginate(50);
        return view('paginated', compact('nodes'));
    }
}

Blade sample code:

<div class="page">
    <p>
        This will be displayed only at first load.
        Javascript will load a new whole page on infinite scroll,
        but will get only the contents of .infinite-scroll div
    </p>

    <div class="container">
        <div class="infinite-scroll">
            @foreach($nodes as $node)
                <div class="row mb-1">
                    <div class="col">
                        {{ $node->foo }}
                    </div>
                    <div class="col">
                        {{ $node->bar }}
                    </div>
                    <div class="col">
                        {{ $node->baz }}
                    </div>
                </div>
            @endforeach

            {{ $nodes->links() }}
        </div>
    </div>
</div>

Javascript code:

/*
    Simplified version of
    https://gist.github.com/harryfinn/75e0e3ea48987b7d0c0a
*/
function onscreen(node) {
    var vbottom = $(window).scrollTop() + $(window).height() + 200;
    var bottom = node.offset().top + node.outerHeight();
    return (bottom <= vbottom);
}

$('.infinite-scroll').each(function() {
    var scrollable = $(this);
    var paginator = scrollable.find('.pagination');
    var loading = false;

    $(window).on('scroll', function () {
        /*
            The paginator is used as trigger to execute the next load:
            when it is visible, I am at the bottom of the page
        */
        if (onscreen(paginator) && loading == false) {
            loading = true;
            var next = paginator.find('li.active + li a');
            paginator.remove();

            $.ajax({
                url: next.attr('href'),
                method: 'GET',
                dataType: 'HTML',
                success: function(data) {
                    var d = $(data).find('.infinite-scroll');
                    scrollable.append(d.children());
                    paginator = scrollable.find('.pagination');
                    loading = false;
                }
            });
        }
    });
});

Here we go, ready for the next lazy developer looking for my same stuff...

Update: I've added this feature directly into jBob, my JS library of jQuery + Bootstrap utilities, for even more lazy developers who want an out-of-the-box experience.