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
- 1
- 2