Advertisement

Getting Loopy - Ajax Powered Loops with jQuery and WordPress

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

In this tutorial, we give you a starting point for creating AJAX interaction in your blog. We follow a step by step process, showing you how to load posts based on the viewers page scroll. The tutorial covers enqueueing scripts, setting up an AJAX handler, how to get a file outside of WordPress to use WordPress functions and access the database, and logic for loading posts on user page scroll.


What this tutorial will cover:

  1. Organizing folders.
  2. Adding jQuery to your site.
  3. Including a new script (js) file.
  4. Adding a loop handler php file (loop.php or loopHandler.php).
  5. Copying loop from main index file to our loop handler.
  6. Cleaning up the loop.
  7. Including wp-load.php (it’s what makes this whole thing work well)
  8. Storing GET data as variables in our handler.
  9. Using wp-query.
  10. Using jQuery, create our AJAX call function.
  11. Success!
  12. Displaying data on page scroll.
  13. Adding a loader gif.

Assumptions:

  • You are familiar with jQuery JavaScript library
  • You have some basic understanding of AJAX
  • You are comfortable with editing WordPress PHP files

Why a Child Theme?

Since the lessons in this tutorial can be applied to any theme, I do not want to go in depth about creating child themes (as that is an article in itself). However, we do not want to lose all the hard work we will be doing here, we will need to create a child theme. If you are not familiar with child themes, you can look up how to create them by searching the WordPress codex.

For this tutorial, we will be using the the default “twentyeleven,” theme, which comes bundled with WordPress, as the parent for our child theme. All files we will be editing and creating will reference this theme, but the concepts discussed here can be applied to any WordPress loop, regardless of theme. OK, let's compile our assets and begin editing. As we move through the code, sections will be repeated and new additions will be highlighted in bold.


The Screencast

Follow along, as we walk you step-by-step through the code, or follow the written steps below.


Step 1 Organizing folders

When you have finished adding your child theme folder, you should only see a stylesheet, linking to the parent theme, inside. Inside your folder add the following:

  • A folder named “js”
  • A folder named “images”
  • An empty php file named loopHandler.php
  • An empty php file named functions.php
  • Inside the “js” folder, add an empty js file named ajaxLoop.js
  • Copy the Index file of the parent theme and paste it into the child theme root folder

Functions.php will not override the parent theme’s functions file, but rather extend it. This file will allow us to properly add scripts to our page.


Step 2 Adding jQuery to your site

By default, jQuery is not automatically added to the “twentyeleven” theme. We can add it to our theme by adding a function to our functions.php file we created and then calling add_action() to register it. Add the following code to your functions.php file:

<?php
function register_jquery() {
    wp_deregister_script( 'jquery' );
    wp_register_script( 'jquery', 'http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js');
    wp_enqueue_script( 'jquery' );
}     
add_action('wp_enqueue_scripts', 'register_jquery');
?>

With wp_deregister_script(), we make sure to unregister any previous jquery script (if any), then we proceed to using wp_register_script() to add jQuery. We name the registered script in the first argument, “jquery” and we set the source to Google cdn in the second argument. We then call wp_enqueue_script() with the name we set in the previous function (“jquery”). The add_action() function will finally register the function we created using the hook “wp_enqueue_scripts”. According to the codex, using “wp_enqueue_scripts” as the hook is safer than using hooks such as “init”, which may break things after upgrading.

UPDATE: "WordPress 3.2 core ships with jQuery 1.6.1 out of the box so you could remove lines 3 & 4 from that and just have wp_enqueue_script( ‘jquery’ ); and WordPress 3.3 ships with jQuery 1.7.1. If you're just doing that in case the user has a cached copy of jQuery from Google's CDN then that's cool but if you're doing that you should have a fallback just in case the CDN happens to go down. I know the chances of that are very small but it's best to be safe" - Thanks to Bronson Quick in the comments for this note! The revised code would look like this:

<?php
function register_jquery() {
    wp_enqueue_script( 'jquery' );
}     
add_action('wp_enqueue_scripts', 'register_jquery');
?>

Japh adds: "WordPress ships with jQuery and unless you’re deregistering it and reregistering it for the CDN benefits, there’s no need to do anything more than enqueuing jQuery.

The method described above is covered in the WordPress Codex under Load a default WordPress script from a non-default location, and there’s actually a note there recommending against it.

In fact, usually you’re including jQuery for use with another script, so you could simply indicate jQuery as a dependancy for your script in it’s enqueue and voila! This is covered on that Codex page too, under Load a script from your theme which depends upon a WordPress Script".

You can read all about the proper use of using the enqueue script in our post on it here.


Step 3 Including a New Script (js) File.

To add our custom script, we will be following the same steps as we did for adding jQuery. The main difference will be that we are going to add a locally stored file rather than one that is hosted by Google. Because this script will be dependent on jQuery being loaded, we will add another argument to our register_script() call. The following is the full code in your functions.php file:

<?php
function register_jquery() {
    wp_deregister_script( 'jquery' );
    wp_register_script( 'jquery', 'http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js');
    wp_enqueue_script( 'jquery' );
}
add_action('wp_enqueue_scripts', 'register_jquery');

function register_ajaxLoop_script() {
    wp_register_script(
      'ajaxLoop',
       get_stylesheet_directory_uri() . '/js/ajaxLoop.js',
       array('jquery')
    );
    wp_enqueue_script('ajaxLoop');
}
add_action('wp_enqueue_scripts', 'register_ajaxLoop_script');
?>

The third argument, array(‘jquery’) handles any dependencies that your script may have. You can pass additional items in the array for each additional dependency. Please note that if you did not make a child theme, you should use "get_template_directory_uri()" instead of "get_stylesheet_directpry_uri()".


Step 4 Copying Loop from Main Index File to our Loop Handler

Now that we have our scripts queued, we can begin writing our code. First we need to take a look at the index.php file inside our child theme folder. Open the file and highlight the following code:

<?php if ( have_posts() ) : ?>
    <?php twentyeleven_content_nav( 'nav-above' ); ?>
    <?php /* Start the Loop */ ?>
    <?php while ( have_posts() ) : the_post(); ?>
        <?php get_template_part( 'content', get_post_format() ); ?>
    <?php endwhile; ?>
    <?php twentyeleven_content_nav( 'nav-below' ); ?>
<?php else : ?>
    <article id="post-0">
        <header>
            <h1><?php _e( 'Nothing Found', 'twentyeleven' ); ?></h1>
        </header><!-- .entry-header -->
        <div>
            <p><?php _e( 'Apologies, but no results were found for the requested archive. Perhaps searching will help find a related post.', 'twentyeleven' ); ?></p>
            <?php get_search_form(); ?>
        </div><!-- .entry-content -->
    </article><!-- #post-0 -->
<?php endif; ?>

Copy this code and then paste it into the loopHandler.php file you created. After copying the code to your loopHandler.php file, make sure to remove this code from your index.php


Step 5 Cleaning Up the Loop

We will now clean this code up. Primarily, we will strip the code of anything we will not need. I have personal preferences in how I format my code, but you are free to format it any way that works for you. The main thing we want to accomplish here is to only keep the necessary and discard the rest. This is what we will end up with:

<?php 

// our loop
if (have_posts()) {
 while (have_posts()){
 the_post();
 get_template_part( 'content', get_post_format() );
 }
}
?>

As you can see here, we have broken the code down to its basics. I prefer to use the standard “if”, “else” syntax, but again, you can format it as you wish. We now have the guts that we need to get the posts from our database.


Step 6 Including wp-load.php (it’s what makes this whole thing work well)

One thing that stands in our way, at this point, is that our loopHandler.php file has no knowledge of WordPress or WordPress functions on its own. To mitigate this, we have to add a couple lines of code which will include the necessary data. WordPress made this easy for us by giving us wp-load.php file. It is located in the main directory of our installation. We will add the code above our loop. Our file should now look like the following:

<?php
// Our include
define('WP_USE_THEMES', false);
require_once('../../../wp-load.php');

// our loop
if (have_posts()) {
       while (have_posts()){
              the_post();
              get_template_part( 'content', get_post_format() );
       }
}
?>

We need to define our WP_USE_THEMES constant to false, so that we do not load any extra data than we need. We then require_once() our wp-load.php. Since we put our loopHandler.php in the root of our theme folder, it’s easy enough to use a relative path to wp-load.php. It’s only three levels up :).

"Since we put our loopHandler.php in the root of our theme folder, it’s easy enough to use a relative path to wp-load.php. It’s only three levels up :)"


Step 7 Storing GET Data as Variables in Our Handler.

The next step is to add a few lines to grab GET data that we will later send. We want to add two variables: One that will store the requested number of pages and one that will store the “page” number. The reason I wrote “page” in quotes is that we will not truly have pages but a single page that will continue loading more and more posts as we scroll down. However, when we query the database, we will send it a page to fetch. You will see how this works later in this tutorial. Let’s add our variables. Our code should now look like the following:

<?php
// Our include
define('WP_USE_THEMES', false);
require_once('../../../wp-load.php');

// Our variables
$numPosts = (isset($_GET['numPosts'])) ? $_GET['numPosts'] : 0;
$page = (isset($_GET['pageNumber'])) ? $_GET['pageNumber'] : 0;

// our loop
if (have_posts()) {
       while (have_posts()){
              the_post();
              get_template_part( 'content', get_post_format() );
       }
}
?>

If you are not familiar of the short hand “if” statements, this might look funky to you, but it is the same as creating an “if” statement. In the parenthesis we have our condition (is numPost set in the $_GET global variable?), then we follow it with a question mark. The left side of the colon signifies what will happen if our condition is true and the right side signifies what will happen if our condition is false. It is essentially the same as writing the following:

if( isset($_GET['numPosts']) ) {
    $numPosts = $_GET['numPosts'];
} else {
    $numPosts = 0;
}
if( isset($_GET['pageNumber']) ) {
    $page = $_GET['pageNumber'];
} else {
    $page = 0;
}

Step 8 Using wp-query.

Now we need to add just a couple more lines of code. To be able to query the database we need to create a new query using the query_posts() function. We need to pass an array of arguments to the query which will request the number of pages and the page number we wish to get. We will query the database, loop through the results, and then we will need to reset the query afterwards for good measure. Our code will now look like the following:

 <?php
// Our include
define('WP_USE_THEMES', false);
require_once('../../../wp-load.php');

// Our variables
$numPosts = (isset($_GET['numPosts'])) ? $_GET['numPosts'] : 0;
$page = (isset($_GET['pageNumber'])) ? $_GET['pageNumber'] : 0;

query_posts(array(
       'posts_per_page' => $numPosts,
       'paged'          => $page
));

// our loop
if (have_posts()) {
       while (have_posts()){
              the_post();
              get_template_part( 'content', get_post_format() );
       }
}
wp_reset_query();
?>

Step 9 Using jQuery, Create Our AJAX Call Function.

Now that we have a decent base to work with, let’s add some javascript to our ajaxLoop.js. First let’s add a jQuery function that waits for the document to load. We will then add our script inside. In your ajaxLoop.js file, add the following lines of code:

// ajaxLoop.js
jQuery(function($){
   // we will insert code here
});

 

This function wrap is the same as calling jQuery(‘document’).ready(function($){});. What we are doing here is calling jQuery rather than the dollar symbol ($), and the passing the dollar symbol to the function so that we can use it as an alias for jquery inside this function. We do this in case you are using $.noConflict() with other script libraries that may use the dollar symbol. If you are not sure what $.noConflict() is, you can search the jQuery documentation for further explanation. Next, let’s set up some variables:

// ajaxLoop.js
jQuery(function($){
    var page = 1;
    var loading = true;
    var $window = $(window);
    var $content = $('body.blog #content');
});

The “page” variable will store which page we will be loading and will increase each time we load more content. Our second variable “loading” will keep track of the content status. When we will write our function to check the scroll of the window, we do not want it to execute if we are in the loading state. Since we will load our first “page” automatically, we will set loading to “true”. The next variable “$window” will store the window DOM element after we query it with jQuery. Storing this query in a variable is useful because we do not have to make additional calls to jQuery and it helps improve performance.

Our last variable “$content” will store the returned element of the container we wish to use for loading our content. In WordPress, we are provided a class on the “body” element for the blog page called “blog”. Here I targeted the content that belongs to the blog page only so that other pages will not be affected. There are better ways of accomplishing this, but for the purposes of this tutorial, this is the quickest.

Now we need to create our AJAX call function. After setting up theAJAX function your code should look like the following:

// ajaxLoop.js
jQuery(function($){
    var page = 1;
    var loading = true;
    var $window = $(window);
    var $content = $('body.blog #content');
    var load_posts = function(){
            $.ajax({
                type       : "GET",
                data       : {numPosts : 1, pageNumber: page},
                dataType   : "html",
                url        : "http://WWW.YOURSITE.COM/wp-content/themes/YOUR_THEME_NAME/loopHandler.php",
                beforeSend : function(){
                },
                success    : function(data){
                },
                error     : function(jqXHR, textStatus, errorThrown) {
                    alert(jqXHR + " :: " + textStatus + " :: " + errorThrown);
                }
        });
    }
});

We stored the load_posts() function as a variable and can reuse it as necessary. So far we have just created the Shell for the call. When we make a jQuery AJAX call, we pass an object as the argument. The object consists of key-value pairs which will be the options we set. First we set the ‘type’ to use ‘GET’. Second we set the ‘data’ option to another object of key-value pairs consisting of our options (numPosts and pageNumber).

The ‘dataType’ option specifies the type of data that will be returned from the server. In this case it will be ‘HTML’. The ‘url’ option points to our loopHandler.php file. This is the file it will request data from. You must replace WWW.YOURSITE.COM with your site url and then replace YOUR_THEME_NAME with the name you have given your child theme!

"You must replace WWW.YOURSITE.COM with your site url and then replace YOUR_THEME_NAME with the name you have given your child theme."

Then, we have setup options for three callbacks: ‘beforeSend’, ‘success’ and ‘error’. We will pass a parameter (I named it ‘data’) to the success function and this will be the returned data object from the server. We will be setting up this function soon.

Our ‘error’ callback will handle any server errors returned. For the purposes of this tutorial we will just output the error messages. In the ‘beforeSend’ callback we will add logic to create an animated loading gif to display while the data is being requested.


Step 10 Adding the 'Success!' Callback

Now we need to build our ‘success’ callback function. First, we will want to hide the data before it is shown so that we can nicely fade it in. We will then append the data to our content and fade it in. Let’s modify our ajaxLoop.js file to have the following:

// ajaxLoop.js
jQuery(function($){
    var page = 1;
    var loading = true;
    var $window = $(window);
    var $content = $("body.blog #content");
    var load_posts = function(){
            $.ajax({
                type       : "GET",
                data       : {numPosts : 1, pageNumber: page},
                dataType   : "html",
                url        : "http://WWW.YOURSITE.COM/wp-content/themes/YOUR_THEME_NAME/loopHandler.php",
                beforeSend : function(){
                },
                success    : function(data){
                    $data = $(data);
                    $data.hide();
                    $content.append($data);
                    $data.fadeIn(500);
                },
                error     : function(jqXHR, textStatus, errorThrown) {
                    alert(jqXHR + " :: " + textStatus + " :: " + errorThrown);
                }
        });
    }
});

So far, we have passed the data to jQuery ($(data)) and stored it in a variable we named $data. We used the jQuery function to hide the data. We then append the data to our content container. Once the data is appended, we fade it in.


Step 11 Displaying the Data on Page Scroll.

Now we will add three more items. The first will be a function which handles loading posts, that will be called on page scroll. The second will call our load_posts() function for the first time. In our page scroll handler we need to check to see if data is currently waiting to be loaded, call our load_posts() function and set our “loading” variable to ‘true’. For our third item, we will set the “loading” variable to ‘false’ in the callback of our fadeIn() function call. Let’s add the code as follows:

 // ajaxLoop.js
jQuery(function($){
    var page = 1;
    var loading = true;
    var $window = $(window);
    var $content = $("body.blog #content");
    var load_posts = function(){
            $.ajax({
                type       : "GET",
                data       : {numPosts : 1, pageNumber: page},
                dataType   : "html",
                url        : "http://WWW.YOURSITE.COM/wp-content/themes/YOUR_THEME_NAME/loopHandler.php",
                beforeSend : function(){
                },
                success    : function(data){
                    $data = $(data);
                    $data.hide();
                    $content.append($data);
                    $data.fadeIn(500, function(){
                        loading = false;
                    });
                },
                error     : function(jqXHR, textStatus, errorThrown) {
                    alert(jqXHR + " :: " + textStatus + " :: " + errorThrown);
                }
        });
    }
    $window.scroll(function() {
        var content_offset = $content.offset(); 
        console.log(content_offset.top);
        if(!loading && ($window.scrollTop() + 
            $window.height()) > ($content.scrollTop() +
            $content.height() + content_offset.top)) {
                loading = true;
                page++;
                load_posts();
        }
    });
    load_posts();
});

Here we put in the logic to check the location of our scroll relative to the end of the content container. Before every call to the load_posts() function we add one to our page variable, so that we get new data. The reason we add to the page before the load_posts() function call is that we have already called it once during the initialization of the script. We also set loading to true, so that we do not make additional calls before our new data is loaded.


Step 12 Adding a Loader gif.

Finally we will add a loader gif, so that on every load, the user has some feedback that the page is processing data. We will also include a check to make sure that the first load does not show the gif. In our success and error callback functions, we will remove the gif so that it doesn’t stay spinning on screen.

First things first, we need to add a loader.gif to our images folder. I suggest visiting http://www.ajaxload.info/ where you can download the loading gif of your liking. Save the image as ajax-loader.gif inside the images folder we created within our child theme’s root folder. We then will add the code to our ajaxLoop.js file that will handle this loader. Our file will now look like the following:

// ajaxLoop.js
jQuery(function($){
    var page = 1;
    var loading = true;
    var $window = $(window);
    var $content = $("body.blog #content");
    var load_posts = function(){
            $.ajax({
                type       : "GET",
                data       : {numPosts : 1, pageNumber: page},
                dataType   : "html",
                url        : "http://WWW.YOURSITE.COM/wp-content/themes/YOUR_THEME_NAME/loopHandler.php",
                beforeSend : function(){
                    if(page != 1){
                        $content.append('<div id="temp_load" style="text-align:center">\
                            <img src="../images/ajax-loader.gif" />\
                            </div>');
                    }
                },
                success    : function(data){
                    $data = $(data);
                    if($data.length){
                        $data.hide();
                        $content.append($data);
                        $data.fadeIn(500, function(){
                            $("#temp_load").remove();
                            loading = false;
                        });
                    } else {
                        $("#temp_load").remove();
                    }
                },
                error     : function(jqXHR, textStatus, errorThrown) {
                    $("#temp_load").remove();
                    alert(jqXHR + " :: " + textStatus + " :: " + errorThrown);
                }
        });
    }
    $window.scroll(function() {
        var content_offset = $content.offset();
        console.log(content_offset.top);
        if(!loading && ($window.scrollTop() +
            $window.height()) > ($content.scrollTop() +
            $content.height() + content_offset.top)) {
                loading = true;
                page++;
                load_posts();
        }
    });
    load_posts();
});

We have also added a check to make sure that if there is no data, the loading gif is removed. When our last call is sent to the server and there are no more posts, we will no longer be setting our “loading” variable to ‘false’. We will be in a loading state from then on and our scroll function will no longer make any more calls to our load_posts() function. At the same time, we make sure to clean up the last ajax-loader gif.


Wrapping Up

This tutorial was meant as an example of what can be done with posts inside of WordPress using AJAX and jQuery. We only cover a basic example of what can be accomplished here, but have provided some important concepts to take away from this. The major points to look at are how we included wp-load.php, queuing our scripts in WordPress and handling our AJAX calls.


About the Authors

Dennis Baskin

Dennis Baskin is a front-end web developer and designer working at the heart of Silicon Valley. He specializes in javascript and jQuery, but enjoys a variety of programming languages. Through his career, he has developed a variety of customized Wordpress installs for clients and loves working with the framework. For more information, you can find him on LinkedIn.

Jerry Bates

Jerry Bates is a freelance developer, designer, and WordPress geek since 2.6 days. He currently creates WordPress-powered websites through his business, FittingSites, and he also publishes video tutorial content for new users of WordPress on his YouTube Channel. You can follow him on Twitter @JerrySarcastic

Advertisement