How to dynamically load posts on click using Ajax in Genesis

This members-only tutorial provides the steps to show post title links in the sidebar and using Ajax to fetch and load the corresponding entry when a title is clicked based on this excellent article from wpmudev.

While the tutorial has been written for Genesis Sample, it should work with a few adjustments in any Genesis or WordPress theme.

The process flow for making an Ajax call in WordPress can be summarized like so:

Step 1: The PHP

Let’s create a custom Page template having the code to

  • force sidebar-content layout.
  • relocate the entry header from its default position to before the content-sidebar wrap.
  • remove primary sidebar from the primary sidebar area.
  • use a custom WP_Query to output linked titles of all the posts in the primary sidebar area.
  • pass admin-ajax.php’s URL and a loading image URL to `load-post.js` and load it in the footer.

Create a file named say, template-ajax-load.php in the child theme directory having the following:

/* Template Name: AJAX Load */

// Forces sidebar-content layout setting.
add_filter( 'genesis_pre_get_option_site_layout', '__genesis_return_sidebar_content' );

/* Relocates entry header */
remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_open', 5 );
remove_action( 'genesis_entry_header', 'genesis_do_post_title' );
remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_close', 15 );

add_action( 'genesis_before_content_sidebar_wrap', 'genesis_entry_header_markup_open' );
add_action( 'genesis_before_content_sidebar_wrap', 'genesis_do_post_title' );
add_action( 'genesis_before_content_sidebar_wrap', 'genesis_entry_header_markup_close' );

// Removes Primary Sidebar from the Primary Sidebar area.
remove_action( 'genesis_sidebar', 'genesis_do_sidebar' );

add_action( 'genesis_sidebar', 'sk_show_post_links' );
/**
 * Outputs links to all the posts in the Primary Sidebar area.
 */
function sk_show_post_links() {

    // WP_Query arguments.
    $args = array(
        'posts_per_page' => '-1',
        // 'post_type' => 'gs_faq',
    );

    // The Query.
    $query = new WP_Query( $args );

    // The Loop.
    if ( $query->have_posts() ) {
        echo '<ul>';
        while ( $query->have_posts() ) {
            $query->the_post();
            printf( '<li><a href="%s" class="post-link" data-id="%s">%s</a></li>',
                esc_url( get_the_permalink() ),
                get_the_ID(),
                esc_html( get_the_title() )
            );
        }
        echo '</ul>';
    } else {
        // no posts found.
    }

    // Restore original Post Data.
    wp_reset_postdata();

}

add_action( 'wp_enqueue_scripts', 'sk_load_post_script' );
/**
 * Passes WP AJAX URL and loading image URL to `load-post.js` and loads it in the footer.
 */
function sk_load_post_script() {

    wp_enqueue_script(
        'sk-loadpost',
        get_stylesheet_directory_uri() . '/js/load-post.js',
        array( 'jquery' ),
        CHILD_THEME_VERSION,
        true
    );

    wp_localize_script(
        'sk-loadpost',
        'sk_ajaxobject',
        array(
            'ajaxurl'       => admin_url( 'admin-ajax.php' ),
            'loadingimage'  => get_stylesheet_directory_uri() . '/images/loading.gif',
        )
    );

}

genesis();

Note:

a) If you wish to have the solution work with entries of a Custom Post Type instead of the standard posts, uncomment

'post_type' => 'gs_faq',

and specify your post type.

b) We are setting a class of post-link for each title link and adding the ID of that entry as the value of data-id attribute. Later in the JS file, we will retrieve this ID and pass it along in our Ajax request to the server.

c) In the sk_load_post_script() function, we are sending the URL of WordPress’s main Ajax handler, wp-admin/admin-ajax.php and that of a loading indicator image onto load-post.js (which, we are going to create next).

d) Upload loading.gif image (downloaded from here) to child theme’s images directory.

Step 2: The Javascript

Create a file named load-post.js in child theme’s js directory having the following:

(function ($) {

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

        $.ajax({
            // URL to which request is sent.
            url: sk_ajaxobject.ajaxurl,

            // specifies how contents of data option are sent to the server.
            // `post` indicates that we are submitting the data.
            type: 'post',

            // data to be sent to the server.
            data: {
                // a function defined in functions.php hooked to this action (with `wp_ajax_nopriv_` and/or `wp_ajax_` prefixed) will run.
                action: 'sk_load_post',

                // stores the value of `data-id` attribute of the clicked link in a variable.
                post_id: $(this).data('id')
            },

            // pre-reqeust callback function.
            beforeSend: function () {
                $('.content > .entry .entry-content').html('<img src="' + sk_ajaxobject.loadingimage + '" />');
            },

            // function to be called if the request succeeds.
            // `response` is data returned from the server.
            success: function (response) {
                $('.content > .entry .entry-content').hide().html(response).fadeIn('slow');
            }
        })
    })

})(jQuery);

The Ajax call gets triggered by the click event everytime any of the post title links are clicked.

Note the name of the action, sk_load_post. In the next step, we are going to define the code that should be output on the frontend in a function hooked to this action. The output of this function is what gets sent back to the .js file as the response data.

Steps 3 and 4: functions.php

Add the following in child theme’s functions.php:

add_action( 'wp_ajax_nopriv_sk_load_post', 'sk_load_post' );
add_action( 'wp_ajax_sk_load_post', 'sk_load_post' );
/**
 * Outputs entry header, entry content and entry footer for the specified post.
 */
function sk_load_post() {

    $args = array(
        'posts_per_page' => '1',
        'no_found_rows' => true,
        // set post id to the ID of the post whose title has been clicked via `load-post.js`.
        'p' => intval( $_POST['post_id'] ),
        'post_type' => get_post_type( $_POST['post_id'] ),
    );

    $loop = new WP_Query( $args );

    // sets up the post, sets the ‘in the loop’ property to true.
    $loop->the_post();

    /* entry header */
    do_action( 'genesis_entry_header' );

    /* entry content */
    do_action( 'genesis_before_entry_content' );

    printf( '<div %s>', genesis_attr( 'entry-content' ) );
    do_action( 'genesis_entry_content' );
    echo '</div>';

    do_action( 'genesis_after_entry_content' );

    /* entry footer */
    do_action( 'genesis_entry_footer' );

    // restores original post data.
    wp_reset_postdata();

    // prevents echoing out `0` via the die function in admin-ajax.php, in addition to the above output.
    die();

}

Note:

a) Functions hooked to wp_ajax_nopriv_{action_name} run on the frontend for users that are not logged in and functions hooked to wp_ajax_{action_name} run on the frontend for users that are logged in.

b) If you wish to display the entire post incl. the comments section, author box (if enabled) etc, replace

// sets up the post, sets the ‘in the loop’ property to true.
$loop->the_post();

/* entry header */
do_action( 'genesis_entry_header' );

/* entry content */
do_action( 'genesis_before_entry_content' );

printf( '<div %s>', genesis_attr( 'entry-content' ) );
do_action( 'genesis_entry_content' );
echo '</div>';

do_action( 'genesis_after_entry_content' );

/* entry footer */
do_action( 'genesis_entry_footer' );

// restores original post data.
wp_reset_postdata();

with

genesis_custom_loop( $args );

Step 5: CSS

Let’s add CSS to ensure that the links appear above the loaded content 959px and below.

.page-template-template-ajax-load .entry-header {
    margin-bottom: 40px;
}

.sidebar-content .content-sidebar-wrap {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
        -ms-flex-direction: column;
            flex-direction: column;
}

.sidebar-content .sidebar {
    -webkit-box-ordinal-group: 1;
        -ms-flex-order: 0;
            order: 0;
    margin-bottom: 60px;
}

.sidebar-content .content {
    -webkit-box-ordinal-group: 2;
        -ms-flex-order: 1;
            order: 1;
}

To make sure this looks alright from 960px and above, in the 960px min-width media query, add

.content-sidebar-wrap {
    overflow: hidden;
}

.sidebar-content .content-sidebar-wrap {
    -webkit-box-orient: horizontal;
    -webkit-box-direction: normal;
        -ms-flex-direction: row;
            flex-direction: row;
}

.page-template-template-ajax-load .content-sidebar-wrap {
    margin-bottom: 60px;
    min-height: 70vh;
}

Finally, create/edit a static Page, select the AJAX Load Page Template and publish/update.

// This gist is now maintained on github at https://github.com/luetkemj/wp-query-ref

Quicktag in the post content.
'nopaging' => false, // (boolean) - show all posts or use pagination. Default value is 'false', use paging.
'posts_per_archive_page' => 10, // (int) - number of posts to show per page - on archive pages only. Over-rides posts_per_page and showposts on pages where is_archive() or is_search() would be true.
'offset' => 3, // (int) - number of post to displace or pass over.
// Warning: Setting the offset parameter overrides/ignores the paged parameter and breaks pagination. for a workaround see: http://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination
// The 'offset' parameter is ignored when 'posts_per_page'=>-1 (show all posts) is used.
'paged' => get_query_var('paged'), // (int) - number of page. Show the posts that would normally show up just on page X when usinthe "Older Entries" link.
// NOTE: This whole paging thing gets tricky. Some links to help you out:
// http://codex.wordpress.org/Function_Reference/next_posts_link#Usage_when_querying_the_loop_with_WP_Query
// http://codex.wordpress.org/Pagination#Troubleshooting_Broken_Pagination
'page' => get_query_var('page'), // (int) - number of page for a static front page. Show the posts that would normally show up just on page X of a Static Front Page.
// NOTE: The query variable 'page' holds the pagenumber for a single paginated Post or Page that includes the

This site uses cookies to offer you a better browsing experience. By browsing this website, you agree to our use of cookies.