Jquery Infinite Scroll & Backend

Hello!

The newest thing that’s caught my attention has been a jquery infinite scroll plugin. Infinite scroll plugins like that allow the page to populate with new content as the user scrolls down, instead of having to go through next page/previous page buttons. I believe this to be an enhanement in usability and a good idea to employ in a web application. The particular website includes a wordpress plugin as well, but I will show you the jquery plugin.

Although the examples just read text content from a database, you can use them with any HTML element. Images, video, anything!

If you want to read up in the plugin’s documentation, it’s available in the github.
Most options aren’t required, however. These are the options that you have to use:

/* Call this where your dynamic entries
   will be. */
$('#posts').infinitescroll({
    /* The CSS selector of your regular next/previous page
    navigation. This will be hidden when the script 
   loads */
    navSelector  : "#pages", 
    /* The CSS selector of the 'next page' link */
    nextSelector : "#next",
    /* The CSS selector of each item that will be
       appended while scrolling (e.g. your posts)*/
    itemSelector : ".post",
});

Note: The plugin also expects a specific URL format, but it’s pretty flexible: something like /page/2, /page=2 etc will work. However, you need to increase pages by one like that (instead of just giving the server the id of the next record it should load, like page=5 for the second 5 items). I used to do it lazily like that, but it didn’t work out for me ;)

With a configuration like that, your HTML should be something like that:

<div id="posts">
    <div id="pages">
      <a href="/page/1">Previous Page</a> <a href="/page/3" id="next">Next Page</a>
    </div>
    <div class="post">
      First post content
    </div>
    <div class="post">
      Second post
    </div>
    <!-- MOAR !-->
</div>

Now, of course you have to write a backend for it, of course. (Or modify your current backend). I actually wrote two examples, in PHP and Catalyst

A PHP Example

<?php
    $mysqli = new mysqli("host", "username", "password", "database");
 
    # Number of entries to load per page.
    $limit = 5;

    # Get the page number from GET, or set it to 0.
    $index = isset ( $_GET['page'] ) && $_GET['page'] ? (int) $_GET['page'] : 0;

    $page = $index * $limit;

    # Get the total number of posts. We need this to know when we've reached the last
    # page.
    # You probably want to do some error handling here, if mysql queries fail.
    $statement = $mysqli->prepare ("SELECT COUNT(*) FROM posts");
    $statement->execute();

    $statement->bind_result($count);
    $statement->fetch();
    #The result is now in the $count variable.

    $statement->close();

    # Get the posts in that 'page'
    $statement = $mysqli->prepare 
    ("SELECT post_title, post_content FROM posts LIMIT ?, ?");
    $statement->bind_param('ii', $page, $limit);
    $statement->execute();

    $result = $statement->get_result();

    # And start outputting HTML...

    echo '<div id="posts">';

    while ( $row = $result->fetch_array() ) {
       # And output one .post per database row :)
        echo '<div class="post">
          <h1>' . $row['post_title'] . '</h1>        
          <p>' . $row['post_content'] . '</p>
        </div>';
    }

    $statement->close();

    echo '</div>'; # #posts

    echo '<div id="pages">';

    if ($page != 0) {
        echo '<a href="?page=' . ($index-1) . '">Previous Page</a> ';
    } if ($page + $limit < $count) {
        echo '<a id="next" href="?page=' . ($index+1) . '">Next Page</a>';
    }

    echo "</div>";

    # Output the JS too
    echo "
    <script src='jquery-1.10.2.min.js'></script>
    <script src='jquery.infinitescroll.min.js'></script>
 
    <script type='text/javascript'>
    $(document).ready(function() {
        $('#posts').infinitescroll({
            debug: true,
            navSelector  : '#pages',
            nextSelector : '#next',
            itemSelector : '.post',

        });
    });
    </script>";

    $mysqli->close();
?>

And now you see how to implement the plugin in your PHP backend!

A Catalyst Example

I assume here that you use DBIx::Class in Model::DB and View::TT as your view. You can use other things but you’ll have to change the corresponding parts ;)
Oh, and that your controller Test::Controller::Post.

package Test::Controller::Post;
use Moose;
use namespace::autoclean;

BEGIN { extends 'Catalyst::Controller' }

# We'll make a base method for every valid link in /post.
# Here we'll just find our post resultset.

sub base :Chained('/') :PathPart('post') :CaptureArgs(0) {
    my ($self, $c) = @_;

    my $posts_rs = $c->model('DB::Post');
    $c->stash->{posts_rs} = $posts_rs;
}

# Handle /page/[argument]
# The client will load for example /page/5, and our backend
# will get it in the $page variable 

sub page :Chained('base') :PathPart('page') :CaptureArgs(1) {
    my ($self, $c, $page) = @_;

    my $posts_rs = $c->stash->{posts_rs};

    # DBIx makes this so simple

    my $result_rs = $posts_rs->search(
        undef,
        {
            page => $page,
            rows => 5
        }
    );

    # Get the required data that our template will need to render.
    # The current page, first and last page, previous and next page,
    # And of course, the rows in the result set (posts)

    my $pager = $result_rs->pager;
    $c->stash->{current_page} = $pager->current_page;
    $c->stash->{first_page} = $pager->first_page;
    $c->stash->{last_page} = $pager->last_page;

    $c->stash->{previous_page} = $pager->previous_page;
    $c->stash->{next_page} = $pager->next_page;

    my @posts = $result_rs->all;

    if (!scalar @posts) {
        $c->detach('/default');
    }

    $c->stash->{posts} = \@posts;
}

# This will just choose the template for /page/NN

sub view_page :Chained('page') :PathPart('') :Args(0) {
    my ($self, $c) = @_;

    $c->stash->{template} = 'post.tt';
}

# And finally, if someone just goes to /post, redirect them to the first page.

sub index :Chained('base') :PathPart('') :Args(0) {
    my ( $self, $c ) = @_;

    $c->response->redirect($c->uri_for('/post/page/1'));
}

Now you just need a template for it:


<div id="posts">
    [% FOREACH post IN posts %]
        <div class='post'>
            <h1>[% post.post_title | html %]</h1>

            <p>
                [% post.post_content | html %]
            </p>

        </div>
    [% END %]
</div>

<div id="pages">
    [% IF current_page != first_page %]
        <a href="[% c.uri_for ("/post/page/${previous_page}") %]">Previous Page</a>
    [% END %]
    [% IF current_page != last_page %]
        <a id="next" href="[% c.uri_for ("/post/page/${next_page}") %]">Next Page</a>
    [% END %]
</div>
 <script src='[% c.uri_for ("/static/jquery-1.10.2.min.js") %]'></script>

 <script src='[% c.uri_for ("/static/jquery.infinitescroll.min.js") %]'></script>
 
 <script type="text/javascript">
    $(document).ready(function() {
        $('#posts').infinitescroll({
            debug: true,
            navSelector  : "#pages",
            nextSelector : "#next",
            itemSelector : ".post",

        });
    });
</script>

And now you should have a controller and view for it :]
Note that I’m still learning catalyst, so may not be the best solution. I like any improvement comments!

Until next time, bye bye.

Leave a Comment

Your email address will not be published. Required fields are marked *