AJAX load posts on WordPress revamped

Image showing a browser with cogs

It turns out back in 2016 people were crazy about loading posts on their pages using AJAX. For over four years I looked at this post and thought to myself: I should update it. A long time passed since then. I am wiser and know that I should do certain things in a different way. So I decided to revamp this article.

The reason I decided to update the article was actually because of a tweet by Maddy Osman

It looks like I’m up to something here. Well yes and no.

On one hand, this post is my most visited post (477 pageviews in the last 28 days on the 12th of September 2020). But, the information, while technically correct, is a bit outdated. The original post was the result of the StackOverflow question. I answered it using TwentyFifteen theme as an example. And the post used TewentySixteen. So, naturally, the new post should use TwentyTwenty as an example 😄.

I actually have a draft of a post called ‘Deep dive into AJAX in WordPress’, but I never got around to finish it. The article was supposed to be a continuation of this one. Explaining how to use AJAX in the admin pages. I even made a small companion plugin to go along with it. But, as with most things. I didn’t have the time (what a lousy excuse).

So let’s start over.

What is AJAX?

One of the things that users on the web hate is waiting. This is where asynchronicity comes into play. Asynchronicity is the basis of modern web apps. Whether it’s using technologies like AJAX or Fetch API, reloading of any kind is avoided at all costs. When it comes to WordPress, we are usually interested in loading posts without reloading the entire page. Although we can fetch any kind of data this way.

First things first – what actually is AJAX?

AJAX stands for Asynchronous JavaScript And XML. It is a way of using many technologies together. HTML, DOM, JavaScript, XMLHttpRequest(XHR) object among others, to make incremental updates to the user interface, without reloading the browser page. In layman terms: it’s sending and retrieving data from a server without interfering with the behavior of your web page. In our case, we click on a button or scroll down a posts page and fetch new posts from a server.

We can use AJAX for fetching any kind of data from a remote resource and use it to display that data on our web page or single page application.

What we are actually doing is making a JavaScript XMLHttpRequest and sending some instructions to our server. Fetching some data or posting some data to it, and then waiting for the response, which can be successful or not. You should learn how it’s done in pure JavaScript. It’s a good exercise.

Another, more recent, technology used for getting our data asynchronously, is using the Fetch API. The difference between the two technologies (fetch and XMLHttpRequest) is that fetch is a promise based operation. Which is a good way to get rid of callback hell.

The underlining benefit of both methods is that they are asynchronous. This means that the browser isn’t locked while an action is happening. The action of getting data is happening ‘under the hood’. All the while the user is free to browse the UI and interact with it without any interruptions.

In the article, we’ll first be using jQuery’s $.ajax function, which is a wrapper around the XHR object. We use it for several reasons:

  • It’s already an existing method provided in jQuery, so we don’t have to invent our own pure JavaScript methods
  • It’s simple to use and understand
  • jQuery is already bundled in the WordPress core, so why not use it?
  • jQuery’s $.ajax also handles promises

Before we begin, I am aware many people hate jQuery. I don’t see any reason why. It’s in WordPress, it does the job, so what’s the fuss? And no matter how you try to avoid it in WordPress, you can’t. It’ll be a long time until people rewrite the core in vanilla JS. And even then, there is a huge probability that some plugin will enqueue the jQuery from WordPress. So you cannot escape it.

PHP part – preparation

I’ve said that we’ll showcase our AJAX loading on the TwentyTwenty theme. But we don’t want to directly edit the theme. That would mean that we’d loose all our modifications on the theme update. For the purpose of this demo, we’ll create a child theme called Twenty Twenty Ajax. First, create a twentytwenty-ajax folder in your themes folder. Then add style.css and functions.php files. The contents of style.css file are

/*
 Theme Name:   Twenty Twenty Ajax
 Theme URI:    https://example.com/twenty-twenty-ajax/
 Description:  Twenty Twenty Ajax Child Theme
 Author:       Denis Žoljom
 Author URI:   https://madebydenis.com
 Template:     twentytwenty
 Version:      1.0.0
 License:      GNU General Public License v2 or later
 License URI:  http://www.gnu.org/licenses/gpl-2.0.html
 Tags:         blog, one-column, custom-background, custom-colors, custom-logo, custom-menu, editor-style, featured-images, footer-widgets, full-width-template, rtl-language-support, sticky-post, theme-options, threaded-comments, translation-ready, block-styles, wide-blocks, accessibility-ready
 Text Domain:  twentytwentyajax
*/Code language: CSS (css)

We’ll have to add small style changes later on. Then in your functions.php file place

<?php

add_action( 'wp_enqueue_scripts', 'twentytwenty_ajax_enqueue_styles' );

function twentytwenty_ajax_enqueue_styles() {
    $parenthandle = 'twentytwenty-style';
    $theme = wp_get_theme();

    wp_enqueue_style( $parenthandle, get_template_directory_uri() . '/style.css', 
        array(),  // if the parent theme code has a dependency, copy it to here
        $theme->parent()->get('Version')
    );
    
    wp_enqueue_style( 'twentytwenty-ajax-style', get_stylesheet_uri(),
        array( $parenthandle ),
        $theme->get('Version') // this only works if you have Version in the style header
    );
}Code language: PHP (php)

Because we want to load the parent styles (we want to add the functionality to load more posts, not change the style). You can activate the theme, and you’ll see that it looks the same as the parent theme. Now we need to change this a bit. We need to change the way the pagination works – we want to remove it. And we need to identify where we want to load our new posts. So we’ll copy the index.php from the parent theme and remove the pagination.

<?php
/**
 * The main template file
 *
 * This is the most generic template file in a WordPress theme
 * and one of the two required files for a theme (the other being style.css).
 * It is used to display a page when nothing more specific matches a query.
 * E.g., it puts together the home page when no home.php file exists.
 *
 * @link https://developer.wordpress.org/themes/basics/template-hierarchy/
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

get_header();

$cat_id = get_query_var( 'cat' );
?>

<main id="site-content" role="main" class="js-post-container" data-category="<?php echo esc_attr( $cat_id ); ?>" data-search="<?php echo esc_attr( $_GET['s'] ); ?>">

	<?php

	$archive_title    = '';
	$archive_subtitle = '';

	if ( is_search() ) {
		global $wp_query;

		$archive_title = sprintf(
			'%1$s %2$s',
			'<span class="color-accent">' . __( 'Search:', 'twentytwenty' ) . '</span>',
			'&ldquo;' . get_search_query() . '&rdquo;'
		);

		if ( $wp_query->found_posts ) {
			$archive_subtitle = sprintf(
				/* translators: %s: Number of search results. */
				_n(
					'We found %s result for your search.',
					'We found %s results for your search.',
					$wp_query->found_posts,
					'twentytwenty'
				),
				number_format_i18n( $wp_query->found_posts )
			);
		} else {
			$archive_subtitle = __( 'We could not find any results for your search. You can give it another try through the search form below.', 'twentytwenty' );
		}
	} elseif ( is_archive() && ! have_posts() ) {
		$archive_title = __( 'Nothing Found', 'twentytwenty' );
	} elseif ( ! is_home() ) {
		$archive_title    = get_the_archive_title();
		$archive_subtitle = get_the_archive_description();
	}

	if ( $archive_title || $archive_subtitle ) {
		?>

		<header class="archive-header has-text-align-center header-footer-group">

			<div class="archive-header-inner section-inner medium">

				<?php if ( $archive_title ) { ?>
					<h1 class="archive-title"><?php echo wp_kses_post( $archive_title ); ?></h1>
				<?php } ?>

				<?php if ( $archive_subtitle ) { ?>
					<div class="archive-subtitle section-inner thin max-percentage intro-text"><?php echo wp_kses_post( wpautop( $archive_subtitle ) ); ?></div>
				<?php } ?>

			</div><!-- .archive-header-inner -->

		</header><!-- .archive-header -->

		<?php
	}

	if ( have_posts() ) {

		$i = 0;

		while ( have_posts() ) {
			$i++;
			if ( $i > 1 ) {
				echo '<hr class="post-separator styled-separator is-style-wide section-inner" aria-hidden="true" />';
			}
			the_post();

			get_template_part( 'template-parts/content', get_post_type() );

		}
	} elseif ( is_search() ) {
		?>

		<div class="no-search-results-form section-inner thin">

			<?php
			get_search_form(
				array(
					'label' => __( 'search again', 'twentytwenty' ),
				)
			);
			?>

		</div><!-- .no-search-results -->

		<?php
	}
	?>

</main><!-- #site-content -->
<div class="load-more-wrapper">
    <div class="load-more-wrapper--loader js-loader"></div>
    <button class="button button-primary aligncenter js-load-more"><?php esc_html_e('Load more posts', 'twentytwenty-ajax'); ?></button>
</div><!-- .load-more-wrapper -->
<?php wp_nonce_field( 'more_posts_nonce_action', 'more_posts_nonce' ); ?>

<?php get_template_part( 'template-parts/footer-menus-widgets' ); ?>

<?php
get_footer();Code language: HTML, XML (xml)

We’ve added the class js-post-container to the #site-content wrapper. It’s always a good idea to prefix the classes that you’ll use only with your JavaScrip with js- prefix. We won’t use them for styling. We’ve also added the button element instead of the pagination template. And the nonce field for security purposes (we’ll explain this later on).

Since we are editing the index.php, we’ve added the category field, so that we can query the category if we are in the category archive page. And the data-search attribute will account for the search functionality (forshadowing).

Using AJAX with WordPress

To use AJAX with WordPress we need to tap into WordPress’s own AJAX handler called `admin-ajax`. The AJAX is in the WordPress core since version 2.1.0. So it’s been there for quite a long time (since 2007). Its original use was to: handle post autosaves, on the fly post-editing (quick edit), or post comment approvals for instance. Which is why it has the admin prefix.

When making an AJAX call we need to specify the URL to the file which handles our AJAX requests. On the WordPress back end, we can use the JavaScript global variable ajaxurl. Since it is already built into the core. WordPress has this feature since version 2.8. Before that, you’d actually had to include your JavaScript in your PHP and hook it to the admin_footer hook. Which is something you should avoid – always separate your concerns.

To use admin-ajax.php on your front end, we need to localize it first. Localization is a way to expose certain data from the backend to the front end. That way you can use it in your JavaScript. If you’re implementing the AJAX method on the admin page, you can skip this part. WordPress loads it automatically on the admin side. Read more on Codex, or in plugin developer handbook. I’ll also add a way for your AJAX to work with the popular WPML plugin. In functions.php we’ll add our own JS file where we’ll handle ajax loading and localize our admin-ajax.php

add_action( 'wp_enqueue_scripts', 'twentytwenty_ajax_enqueue_scripts' );

function twentytwenty_ajax_enqueue_scripts() {
	$theme_version = wp_get_theme()->get( 'Version' );
	$script_handle = 'twentytwenty-ajax';

	wp_enqueue_script( $script_handle, get_stylesheet_directory_uri() . '/assets/js/index.js',
		array( 'jquery' ),
		$theme_version,
		false
	);

	// Include WPML case.
	if( in_array( 'sitepress-multilingual-cms/sitepress.php', get_option( 'active_plugins' ) ) ){
		$ajaxurl = admin_url( 'admin-ajax.php?lang=' . ICL_LANGUAGE_CODE );
	} else{
		$ajaxurl = admin_url( 'admin-ajax.php');
	}

	wp_localize_script( $script_handle, 'twentyTwentyAjaxLocalization', array(
		'ajaxurl' => $ajaxurl,
		'action' => 'twentytwenty_ajax_more_post',
		'noPosts' => esc_html__('No older posts found', 'twentytwenty-ajax'),
	) );
}Code language: PHP (php)

You see that I added the check if the WPML is present, and in that case, we’ve added the ?lang= attribute at the end of the ajax URL. This is necessary so that the AJAX call will work in different languages. In case you don’t have it, you only need to add the admin_url( 'admin-ajax.php' ) to your code. After that, we’ve localized our variables to the ‘twentytwenty-ajax’ script. The script where we’ll put the AJAX code – here it’s index.js file. We used the handle twentyTwentyAjaxLocalization. Localization will make your ajaxurl, action, and noPosts variables part of the twentyTwentyAjaxLocalization object. That way they will be available to use in our AJAX call.

Developer tools showing the twentyTwentyAjaxLocalization object.
Developer tools showing the twentyTwentyAjaxLocalization object.

Load post functionality

To actually load your posts, you’ll need to create a function you’ll call on AJAX that will render your posts. You can either create a separate file to include it in or put it in the functions.php file at the end. I’ll be doing the latter for the simplicity of it.

We’ll start simple and expand on it as we go


add_action( 'wp_ajax_nopriv_twentytwenty_ajax_more_post', 'twentytwenty_ajax_more_post_ajax' );
add_action( 'wp_ajax_twentytwenty_ajax_more_post', 'twentytwenty_ajax_more_post_ajax' );

function twentytwenty_ajax_more_post_ajax() {
	if ( ! isset( $_POST['more_posts_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['more_posts_nonce'] ), 'more_posts_nonce_action' ) ) {
		return wp_send_json_error( esc_html__( 'Number not only once is invalid', 'twentytwenty-ajax' ), 404 );
	}

	wp_send_json_success( $_POST, 200 );
}Code language: PHP (php)

Notice the wp_ajax_ action hook. When using ajax on the admin pages we only need the wp_ajax_{$action} and not wp_ajax_nopriv_{$action} action hook. This is because when we are on the admin pages we are already logged in. So we don’t need the nopriv action hook which is used for users that are not logged in.

First, we want to be sure that the AJAX call came from the screen that we wanted it to come, with proper authorization. That’s why we are using WordPress nonces. After the nonce check passed, we can do whatever we want in the callback method.

Nonces or number valid only once, are security tokens generated to help protect URLs and forms from misuse. Because we are dealing with sending a directive through JS towards our server, it’s a good practice to have extra security in place. It’s a form of cross-site request forgery (CSRF) prevention.

We can add a bunch of logic to this, but for now, let’s verify that this callback works. We’ll add some code to our index.js file.

jQuery(document).ready(function($) {
	'use strict';

	const $wrapper = $('.js-post-container');
	const $button = $('.js-load-more');
	const $loader = $('.js-loader');
	const $nonce = $('#more_posts_nonce');
	const postsPerPage = $wrapper.find('.js-post:not(.sticky)').length;
        const category = $wrapper.data('category');
	const search = $wrapper.data('search');

	$button.on('click', function(event) {
		loadAjaxPosts(event);
	});

	function loadAjaxPosts(event) {
		event.preventDefault();

		if (!($loader.hasClass('is-loading') || $loader.hasClass('no-posts'))) {
			const postNumber = $wrapper.find('.js-post:not(.sticky)').length;

			$.ajax({
				'type': 'POST',
				'url': twentyTwentyAjaxLocalization.ajaxurl,
				'data': {
					'postsPerPage': postsPerPage,
					'postOffset': postNumber,
					'category': category,
					'search': search,
					'morePostsNonce': $nonce.val(),
					'action': twentyTwentyAjaxLocalization.action,
				},
				beforeSend: function () {
					$loader.addClass('is-loading');
				}
			})
				.done(function(response) {
					console.log(response);
					$loader.removeClass('is-loading');
				})
				.fail(function(error) {

				});
		}
	}
});Code language: JavaScript (javascript)

First, we define our constants – things that will be set when our page loads and don’t change. These are usually containers, buttons, nonce field. We’ve also added a postsPerPage variable, that will define how many posts we’ll load. Finding them out this way ensures you can actually change the number in the Settings menu. We are also excluding sticky posts because we’ll exclude them from the query. They’ll always show at the beginning of the post list. Another way to define posts per page would be to read the setting’s value from the database. We could add this information to a data-postsPerPage attribute that we could then read in our JS.

But wait, what is this .js-post class? Where did that come from?

Remember how we mentioned you should separate your concern? For easier post manipulation, we’d like depend on something concrete. Sure, we could use .type-post class, but it’s better to use js prefixed classes. To be honest, in the beginning of rewriting this I used .post class, but it turns out, not every post has this class. So how do we add this custom class? With a filter. In our functions.php file add


add_filter( 'post_class', 'twentytwenty_ajax_add_js_post_class', 10, 3 );

function twentytwenty_ajax_add_js_post_class( $classes, $class, $post_id ) {
	$classes[] = 'js-post';

	return $classes;
}Code language: PHP (php)

It was that simple. You can change this further, by checking the post type, etc. But this will work for our purposes.

We defined the postNumber variable inside our loadAjaxPosts callback. That way, whenever you click on a load more button, you’ll get the correct number to offset your posts in the query. You can also trigger the load posts by scrolling. In that case, you’ll need either waypoints.js or Intersection Observer API.

Let’s go back to our PHP part.

add_action( 'wp_ajax_nopriv_twentytwenty_ajax_more_post', 'twentytwenty_ajax_more_post_ajax' );
add_action( 'wp_ajax_twentytwenty_ajax_more_post', 'twentytwenty_ajax_more_post_ajax' );

function twentytwenty_ajax_more_post_ajax() {
	if ( ! isset( $_POST['morePostsNonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['morePostsNonce'] ), 'more_posts_nonce_action' ) ) {
		return wp_send_json_error( esc_html__( 'Number not only once is invalid', 'twentytwenty-ajax' ), 404 );
	}

	$posts_per_page = ! empty( $_POST['postsPerPage'] ) ? (int) $_POST['postsPerPage'] : 1;
	$offset = ! empty( $_POST['postOffset'] ) ? (int) $_POST['postOffset'] : 0;
	$category = ! empty( $_POST['category'] ) ? sanitize_text_field( wp_unslash( $_POST['category'] ) ) : '';
        $search = ! empty( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '';

	$query_args = array(
		'post_type' => 'post',
		'post_status' => 'published',
		'perm' => 'readable',
		'posts_per_page' => $posts_per_page,
		'offset' => $offset,
                'post__not_in' => get_option( 'sticky_posts' ),
	);

	if ( ! empty( $category ) ) {
		$query_args['cat'] = $category;
	}

	if ( ! empty( $search ) ) {
		$query_args['s'] = $search;
	}

	$posts_query = new WP_Query( $query_args );

	$posts_out = '';

	ob_start();
	if ($posts_query->have_posts()) {
		while ($posts_query->have_posts()) {
			$posts_query->the_post();

			echo '<hr class="post-separator styled-separator is-style-wide section-inner" aria-hidden="true" />';
			get_template_part( 'template-parts/content', 'post' );
		}
	}

	$posts_out = ob_get_clean();

	wp_reset_postdata();

	wp_send_json_success( $posts_out, 200 );
}Code language: PHP (php)

After nonce checks, we check for other variables we can extract from the $_POST superglobal array. You can always error_log or debug this using Xdebug. Or you can check the dev tools when you click the load more posts button

Developer tools showing the contents of the ajax callback
Developer tools showing the contents of the ajax callback

We have our AJAX action callback ready. Let’s finish the JS part and add some CSS styles so that our loading looks a bit more user friendly.

When we click the button in the front end, we trigger the click event in the JS. That will take the data, and make an AJAX request towards our admin-ajax.php script. Because we sent the action parameter with it, that script will search all wp_ajax_{$hook} (and nopriv) hooks. When it finds the one matching our action it will execute it.
In our callback, we ran a new post query with the parameters, wrapped it in an output buffer. We used output buffering because we will output HTML. Using the wp_send_json_succes() function we will send a response to our AJAX function in JS. You can see the response in the developer tools. Just click the ‘Preview’ or the ‘Response’ tab.

That means we can use it. In our .done() part of the $.ajax method, we can now see what we have in the response (HTML) and append it to the container

.done(function(response) {
    const contents = response.data;
    $wrapper.append(contents);
    $loader.removeClass('is-loading');
})Code language: JavaScript (javascript)

But we also don’t want to load posts if the query doesn’t return anything. That’s why we’ll add

.done(function(response) {
    const contents = response.data;

    if (contents.length) {
        $wrapper.append(contents);
        $loader.removeClass('is-loading');
    } else {
        $button.html(twentyTwentyAjaxLocalization.noPosts);
	$loader.removeClass('is-loading');
	$loader.addClass('no-posts');
    }
})
.fail(function(error) {
    console.error(error)
});Code language: PHP (php)

And that’s it. The .fail()method is there in case something bad happens to our response. In our case, only ‘bad’ response would be if the number not only once isn’t valid. You can attach a message in this case, or not. That’s up to you.

We’ll add some nice style to our load more loader

.load-more-wrapper {
    display: flex;
    flex-direction: column;
}

.load-more-wrapper--loader,
.load-more-wrapper--loader:before,
.load-more-wrapper--loader:after {
    border-radius: 50%;
    width: 2.5em;
    height: 2.5em;
    -webkit-animation-fill-mode: both;
    animation-fill-mode: both;
    -webkit-animation: load7 1.8s infinite ease-in-out;
    animation: load7 1.8s infinite ease-in-out;
}

.load-more-wrapper--loader {
    color: #cd2653;
    font-size: 10px;
    margin: 20px auto;
    display: none;
    position: relative;
    text-indent: -9999em;
    -webkit-transform: translateZ(0);
    -ms-transform: translateZ(0);
    transform: translateZ(0);
    -webkit-animation-delay: -0.16s;
    animation-delay: -0.16s;
}

.load-more-wrapper--loader.is-loading {
    display: block;
}

.load-more-wrapper--loader:before,
.load-more-wrapper--loader:after {
    content: '';
    position: absolute;
    top: 0;
    left: -3.5em;
}

.load-more-wrapper--loader:before {
    -webkit-animation-delay: -0.32s;
    animation-delay: -0.32s;
}

.load-more-wrapper--loader:after {
    left: 3.5em;
}

@-webkit-keyframes load7 {
    0%,
    80%,
    100% {
        box-shadow: 0 2.5em 0 -1.3em;
    }
    40% {
        box-shadow: 0 2.5em 0 0;
    }
}

@keyframes load7 {
    0%,
    80%,
    100% {
        box-shadow: 0 2.5em 0 -1.3em;
    }
    40% {
        box-shadow: 0 2.5em 0 0;
    }
}Code language: CSS (css)

Replacing jQuery $.ajax with Fetch API?

The last thing before I go is to show how you can fetch posts using Fetch API. We can use WordPress REST API to fetch the posts.

First, we’ll change the localization object. In functions.php add

wp_localize_script( $script_handle, 'twentyTwentyAjaxLocalization', array(
        'ajaxurl' => $ajaxurl,
	'action' => 'twentytwenty_ajax_more_post',
	'root' => esc_url_raw( rest_url() ),
	'noPosts' => esc_html__('No older posts found', 'twentytwenty-ajax'),
) );Code language: PHP (php)

If we would use REST API the JavaScript part would look something like this

jQuery(document).ready(function ($) {
    'use strict';

    const $wrapper = $('.js-post-container');
    const $button = $('.js-load-more');
    const $loader = $('.js-loader');
    const $nonce = $('#more_posts_nonce');
    const postsPerPage = $wrapper.find('.js-post:not(.sticky)').length;
    const category = $wrapper.data('category');
    const search = $wrapper.data('search');

    $button.on('click', function (event) {
        loadAjaxPosts(event);
    });

    function loadAjaxPosts(event) {
        event.preventDefault();

        if (!($loader.hasClass('is-loading') || $loader.hasClass('no-posts'))) {
            const postNumber = $wrapper.find('.js-post:not(.sticky)').length;

            fetch(twentyTwentyAjaxLocalization.root + 'wp/v2/posts?per_page=' + postsPerPage + '&offset=' + postNumber)
                .then(response => response.json())
                .then(data => {
                    if (data.length) {
                        data.map((post) => {
                            $wrapper.append(post.content.rendered);
                        });
                        $loader.removeClass('is-loading');
                    } else {
                        $button.html(twentyTwentyAjaxLocalization.noPosts);
                        $loader.removeClass('is-loading');
                        $loader.addClass('no-posts');
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }
    }
});Code language: JavaScript (javascript)

The drawback of this approach is that you’d only get post content (blocks). Not the post wrapper, title wrapper, etc. You’d have to construct the article wrapper yourself. We could try to call the admin-ajax.php by manually providing the URL and data

jQuery(document).ready(function ($) {
    'use strict';

    const $wrapper = $('.js-post-container');
    const $button = $('.js-load-more');
    const $loader = $('.js-loader');
    const $nonce = $('#more_posts_nonce');
    const postsPerPage = $wrapper.find('.js-post:not(.sticky)').length;
    const category = $wrapper.data('category');
    const search = $wrapper.data('search');

    $button.on('click', function (event) {
        loadAjaxPosts(event);
    });

    function loadAjaxPosts(event) {
        event.preventDefault();

        if (!($loader.hasClass('is-loading') || $loader.hasClass('no-posts'))) {
            const postNumber = $wrapper.find('.js-post:not(.sticky)').length;
            const data = new FormData();

            data.append('postsPerPage', postsPerPage);
            data.append('postOffset', postNumber);
            data.append('category', category);
            data.append('search', search);
            data.append('morePostsNonce', $nonce.val());
            data.append('action', twentyTwentyAjaxLocalization.action);

            $loader.addClass('is-loading');

            fetch(twentyTwentyAjaxLocalization.ajaxurl, {
                method: 'POST',
                body: data
            })
                .then(response => response.json())
                .then(response => {
                    const contents = response.data;

                    if (contents.length) {
                        $wrapper.append(contents);
                        $loader.removeClass('is-loading');
                    } else {
                        $button.html(twentyTwentyAjaxLocalization.noPosts);
                        $loader.removeClass('is-loading');
                        $loader.addClass('no-posts');
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }
    }
});Code language: JavaScript (javascript)

Notice that you must send the data as a FormData() object in order for it to work.

Did we gain much by replacing the jQuery $.ajax with fetch()? I don’t think so. In this case, it looks like an overkill.

Conclusion

In the article, you’ve seen the correct way to load your posts using the jQuery AJAX function. It’s simple and gives your theme a bit of a flare. You can do a lot with this technology, especially if you couple it with WordPress REST API. In that case, I would opt in using fetch over jQuery, but those cases go beyond the simple WordPress theme.

You can download the code from this article from my GitHub repo (or fork it).

I hope you find this tutorial useful, leave a comment if you have any questions. Happy coding!

106 responses

  1. I’m glad to have played a role in your blogging journey! That Orbit Media blogger survey is a yearly goldmine. Thanks for the mention. :)

    1. Thank you for all the awesome content and useful information you put out for us to use and get inspired :)

  2. Hi Denis, many thanks for sharing the code.

    Everything is working fine on my website except the WPML code part, when I load more projects on a secondary language the loaded posts come from the primary language.

    I tried changing this line of code:
    $ajaxurl = admin_url( ‘admin-ajax.php?lang=’ . ICL_LANGUAGE_CODE );

    For this one, since my language url is website.com/en/page:
    $ajaxurl = admin_url( ‘admin-ajax.php/ . ICL_LANGUAGE_CODE );

    But nothing seems to work.

    Do you have any ideas for what could be failing?

    Many thanks in advance in case you can help out :)

    1. Hi!

      The ajaxurl should work. You can check it in the AJAX callback function to see if it’s triggered when loading new posts. The only thing you’ll have to modify is (probably) the query itself inside the callback.
      I’m not 100% sure if there are any special language parameters that can be added to it to differentiate what language you’ll get returned, but I’m sure there should be some information on line about that :)

  3. […] on AJAX theme I thought I’d show you how to create a simple options page, and save those options using […]

  4. […] the root of your WordPress installation. For instance, two weeks ago I was writing an article about loading posts using AJAX. To be sure my code is ok, I tested it of course. And to my surprise after some time the code […]

  5. Jean Candice Yu Avatar
    Jean Candice Yu

    Hello! Thank you for sharing! I tried to implement it on the website I’m working on but I always get “ReferenceError: ajax_posts is not defined” even after adding the scripts to functions.php and my JS file. What do you think I missed?

    1. Jean Candice Yu Avatar
      Jean Candice Yu

      Here’s the script on functions.php:

      wp_register_script( ‘script_js’, get_template_directory_uri().’/js/script.js’ );
      wp_localize_script( ‘script_js’, ‘ajax_posts’, array(
      ‘ajaxurl’ => admin_url( ‘admin-ajax.php’ )
      ));
      wp_enqueue_script( ‘script_js’ );

      1. Are you sure that your script is actually enqueued? In your console try typing `ajax_posts`, this should give you an object with the ajaxurl. If it’s not there, something has to be wrong with the enqueue function.

  6. agiorgini Avatar
    agiorgini

    Hi, thanks for sharing. I’ve just tried copy/paste your code, just to see it working: in console I get ReferenceError: screenReaderText is not defined. I’m on a 4.9 with transcargo theme. Any help?

    Thanks

    1. Hi! screenReaderText is name that is attached to the twentysixteen-script script handle that is specific for the Twentysixteen theme. You need to use the wp_localize_script() with the handle that responds to the main script in your theme, or the one you have placed your ajax code in :)
      So for instance you can enqueue your own custom.js script like:


      wp_register_script( 'some_handle', 'path/to/custom.js' );
      wp_localize_script( 'some_handle', 'object_name', array(
      'some_string' => __( 'Some string to translate', 'plugin-domain' ),
      'value' => '10'
      ) );
      wp_enqueue_script( 'some_handle' );

  7. Alen Širola Avatar
    Alen Širola

    Hi, Dennis, thanks for sharing your code.
    I have implemented it, but I got bizarre behavior – instead of loading posts – ajax load entire page all over … It’s even stranger that it happens on default themes (twentyseveteen, twentysixteen), but not on my theme, or couple of wp.org themes I tried …
    Any thoughts on where to look …

    1. Usually that means that the callback return is not ok. Could be something in the query. You could post your code on stackovreflow so that I can see it, and try to find the error…

      1. Alen Širola Avatar
        Alen Širola

        Hi, thanks for such a fast reply – it turned out that, after “reverse engineering” TwentySixteen (after testing more then dozen wp.org themes :) ) – it was a question of translatable js vars (wp_localize_scripts) – since you used the “screenReaderText” from enqueued “twentysixteen-script”, that messed up ajax query, seemingly.
        I changed all the instances of “screenReaderText”, and voila … it works. So I would suggest using unique variable name for $name in wp_localize_script( $handle, $name, $data ); .
        Oh, yeah, sorry, I made a separate functionality, didn’t change the twentysixteen … so, you shouldn’t change it in tut, I should have changed that …
        Inače, tek sam naknadno skužio da si iz HR … :Pozdrav iz Rijeke :)

        1. Yeah, this article desperately needs an update. Usually inside the theme I attach the localization to the script I put the ajax call in. In this case I used the default javascript file from twentysixteen…

          Hehe pozdrav iz Zageba :D

  8. Kalyn Bradford Avatar
    Kalyn Bradford

    How would you structure this is you wanted to load a custom post type with custom taxonomies?

    1. Just provide the necessary info for custom post type and taxonomy in the `data` attribute so that you can ‘pick’ them up with javascript and pass them to your query when you call the callback function in your ajax.

  9. Azizul Haque Avatar
    Azizul Haque

    Isn’t nonce important?

    1. Yeah, nonce is very important, but in this case since technically you are only fetching posts back, you can omit it. In cases when you are sending data to the server, using some kind of forms, nonce is crucial.
      I’ve been planing on redoing this tutorial from scratch, cover OOP approach, but I really haven’t find time to do it…

      1. Alen Širola Avatar
        Alen Širola

        Yeah OOP version would be “the real thing” ;)

  10. Justin Estrada Avatar
    Justin Estrada

    Hellyes Thanks a bunch man! Got this working perfectly on a custom theme I wrote! You rock!

    1. Happy that it helped :)

    2. Jay Mastro Avatar
      Jay Mastro

      Have you got a sample link so i can view this code in action?

      1. If you go to the front page, there is a load more button that is actually made using this code :)

  11. michel lompret Avatar
    michel lompret

    Hi Denis could you look at my question on stackoverflow ? http://stackoverflow.com/questions/40759697/ajax-load-more-and-query-in-wordpress

    Thanks

    1. Thanks. I wasn’t quick enough with the SO question, I see that somebody has answered it :)

  12. wimhuiskes Avatar
    wimhuiskes

    Thank you for this tutorial, but i cant seem to get it to work, for the load more posts part so to say.
    I was hoping you could help me by checking my project and point me in the right direction.

    http://www.restaurantbruisblaricum/nieuws

    is what i have so far.

    1. Are you sure you have enough posts? The load more button seems disabled

      1. wimhuiskes Avatar
        wimhuiskes

        Yes, i am sure, there are 7 in total, in the category ‘nieuws’

        1. When I inspected your site there wasn’t any error. Be sure that the javascript code matches your layout (id’s and classes)

      2. wimhuiskes Avatar
        wimhuiskes

        This my javascrip/ajax function:

        var ppp = 4; // Post per page
        var cat = 35;
        var pageNumber = 1;

        function load_posts(){
        pageNumber++;
        var str = ‘&cat=’ + cat + ‘&pageNumber=’ + pageNumber + ‘&ppp=’ + ppp + ‘&action=more_post_ajax’;
        $.ajax({
        type: “POST”,
        dataType: “html”,
        url: ajax_posts.ajaxurl,
        data: str,
        success: function(data){
        var $data = $(data);
        if($data.length){
        $(“#ajax-posts”).append($data);
        $(“#more_posts”).attr(“disabled”,false);
        } else{
        $(“#more_posts”).attr(“disabled”,true);
        }
        },
        error : function(jqXHR, textStatus, errorThrown) {
        $loader.html(jqXHR + ” :: ” + textStatus + ” :: ” + errorThrown);
        }

        });
        return false;
        }

        $(“#more_posts”).on(“click”,function(){ // When btn is pressed.
        $(“#more_posts”).attr(“disabled”,true); // Disable the button, temp.
        load_posts();
        });

        } );

  13. Kabolobari Avatar
    Kabolobari

    What I hoped to find was how this “load more” posts (whatever can be loaded) functionality could be built into a plugin which is portable and can be used on any site to affect any list page of one’s chosen. Is there such a tutorial somewhere? Thanks.

    1. I just saw this comment, disqus didn’t notify me. Well, there are lots of plugins for post loading out there (iirc Jetpack has that built in), but it’s generally tricky to match all the things for this to work with every theme – post containers, post layouts that can be changed etc. I have planned to do a recap of this tutorial, go more in depth, but I am swamped with work atm.

  14. Thank you for the tutorial!
    I was able to load post titles and post thumbnails in my site, using your method.
    But I can not load “echo get_post_meta($post->ID, _aioseop_description, true)”. Actually, I don’t know how to rewrite it (in functions.php).
    Could you tell me how I should rewrite it, if you are fine?
    (I’m not a native English speaker. Sorry if my English is difficult to understand.)

    1. What do you mean rewrite it? Can you post your question on stackowerflow with more details? If you’re loading posts using ajax you should return instead of echoing

      1. I’m sorry to have troubled you.
        But thanks to your answer in this page http://stackoverflow.com/questions/37253810/ajax-request-repeat-the-same-value-from-php , now I can load “get_post_meta”.
        I really thank you for your information.

        1. No problem, glad I could help :)

  15. Hamdi PINAR Avatar
    Hamdi PINAR

    Hello Denis, As I am not a coder, tried tonnes of plugins before to get this function. Your plugin is the only one which worked upon the activation. Many thanks. The only problem, whenever I click the “load more posts” the plugin removes the sidebar of my original theme an loads post snippets and thumbnails with a larger (full screen) view. Is there anything that I can do to fix it?

    1. You must be careful to what you’re appending your post. The above code will append posts in the `.ajax_posts` container, and it shouldn’t remove the sidebar. It’s hard to tell what went wrong without seeing the code, but you can always post your question on SO and link it here so that I can take a look :)

      1. Hamdi PINAR Avatar
        Hamdi PINAR

        I think the problem is that the script doesn’t fetch my blog’s custom css. Anyway, I liked it in that way too. My loaded posts look very unique :)

  16. Hi Denis great tuto but i have a question.
    Why in the function.js you do that

    $loader.removeClass(‘post_loading_loader’);
    $newElements.animate({ opacity: 1 });
    $loader.removeClass(‘post_loading_loader’).html(screenReaderText.loadmore);
    I mean why you removeclass of post_loading_loader twice ?

    1. This seems to be a typo. You can remove the first one. I’ll fix it oce I get to my computer. Thanks :)

  17. roberto solini Avatar
    roberto solini

    Denis
    The modification of post_date by date doesn’t show anything neither but i change the function.js

    from :

    jQuery(document).ready(function($) {
    var ppp = 3; // Post per page
    var offset = $(‘#ajax-posts’).find(‘.posts’).length;
    var $content = $(‘#ajax_posts’);
    var $loader = $(‘#more_posts’);

    $loader.on( ‘click’, load_posts );

    function load_posts(){

    if (!($loader.hasClass(‘post_loading_loader’) || $loader.hasClass(‘post_no_more_posts’))) {

    $.ajax({
    type: “POST”,
    dataType: “html”,
    url: screenReaderText.ajaxurl,
    data: {
    ‘ppp’: ppp,
    ‘offset’: offset,
    ‘action’: ‘more_post_ajax’
    },

    beforeSend : function () {
    $loader.addClass(‘post_loading_loader’).html(”);
    },
    success: function (data) {
    var $data = $(data);
    if ($data.length) {
    var $newElements = $data.css({ opacity: 0 });
    $content.append($newElements);
    $loader.removeClass(‘post_loading_loader’);
    $newElements.animate({ opacity: 1 });
    $loader.removeClass(‘post_loading_loader’).html(screenReaderText.loadmore);
    } else {
    $loader.removeClass(‘post_loading_loader’).addClass(‘post_no_more_posts’).html(screenReaderText.noposts);
    }
    },
    error : function (jqXHR, textStatus, errorThrown) {
    $loader.html($.parseJSON(jqXHR.responseText) + ‘ :: ‘ + textStatus + ‘ :: ‘ + errorThrown);
    console.log(jqXHR);
    },
    });
    }

    return false;
    }

    });

    to

    jQuery(document).ready(function($) {
    var ppp = 3; // Post per page
    var offset = $(‘#ajax-posts’).find(‘.posts’).length;
    var $content = $(‘#ajax_posts’);
    var $loader = $(‘#more_posts’);

    function load_posts(){

    $.ajax({
    type: “POST”,
    dataType: “html”,
    url: screenReaderText.ajaxurl,
    data: {
    ‘ppp’: ppp,
    ‘offset’: offset,
    ‘action’: ‘more_post_ajax’
    },

    success: function(data){
    var $data = $(data);
    if ($data.length) {
    $(“#ajax-posts”).append($data);
    $(“#more_posts”).attr(“disabled”,false);
    } else{
    $(“#more_posts”).attr(“disabled”,true);
    }
    },
    error : function (jqXHR, textStatus, errorThrown) {
    $loader.html($.parseJSON(jqXHR.responseText) + ‘ :: ‘ + textStatus + ‘ :: ‘ + errorThrown);
    console.log(jqXHR);
    },
    });

    return false;
    }
    $(“#more_posts”).on(“click”,function(){ // When btn is pressed.
    $(“#more_posts”).attr(“disabled”,true); // Disable the button, temp.
    load_posts();
    });

    });

    I have no idea why the first one didn’t show anything

  18. roberto solini Avatar
    roberto solini

    Denis I’m trying to get the latest posts but i’ve got some problem to show the posts. I’ve post a question on SO. http://stackoverflow.com/questions/39150087/ajax-load-posts-in-wordpress
    If you got the time to look at it
    Thanks

  19. Jorge Rivero Avatar
    Jorge Rivero

    Is there any way to hide the load more button when the list of posts don’t reach the ppp? For example: My ppp is 9 but I have 6 posts.

    Thanks in advance

    1. The code has a class .no_more_posts you can put display:none so that the button will no longer be visible.

  20. Pankaj Kumar Avatar
    Pankaj Kumar

    What if i already has a post present .. ? The problem is lets take i have 5 post with title.. Posts 1, Post 2, Post 3, Post 4, Post 5 ,, then the problem is if Post 3 is already present and i pressed over the load more link then the same post is getting loaded again ?? how can i escape the existing posts and show the rest ?

    1. This shouldn’t happen. The code keeps track of how many posts are loaded with post per page parameter and the offset parameter. I mean, the same code is utilized on this page as well, and it works so :)

      1. Pankaj Kumar Avatar
        Pankaj Kumar

        It will work if post per page will be one and not 3.. i.e showing one post each time and not 3 after clicking on load more link.

        1. This is odd. Can you post on stack owerflow your code and post a link here so that I can see where the issue could be?

  21. roberto solini Avatar
    roberto solini

    Hi Denis great tuto
    when I read it I immediately want to add to the page builder I use.
    There ‘ s no problem with the php part but there’s some problem with the js part.
    As the page builder allow to have for example 5 more posts from sports category with load more button and 4 more posts from news category with load more button. I think i could store this info like you do with data-category in the load more button so we’ll have

    <div id="more_posts" data-category="” data-number=”x”>

    and then use jquery each function to loop through all data-category and data-number in order to use them in

    function load_ajax_posts(). What do you think of this method ?

    Do you see an easier way of doing that ?

    1. Well multiple logo is possible, but you need to specify what you want to do with them. Load different categories on click, load different posts, or just load new posts by clicking on the same button? It’s all possible, but would require a small modification of the code depending on what you need…

      1. roberto solini Avatar
        roberto solini

        Denis I don’t understand the if ($data.length) in the function.js because it seems to me that there must be a condition like $data.length < something. What condition do you look when you do if ($data.length)

        1. The condition if($data.length) is basically if($data.length>0) It checks if the $data (jQuery object) is empty or not (the data is returned html from the ajax call, and by wrapping it in $(data) we ‘convert’ it to jQuery object for easier manipulation).

  22. Pankaj Gupta Avatar
    Pankaj Gupta

    Hi,
    I want to add load more button on my woocommerce archive (shop) page. Pls help me.

    1. WooCommerce is a bit different, you’d need to disable the pagination, and modify the functions to load post type product, and you’ll need to find a hook to add the load more button. It’s not a trivial task and would probably warrant a tutorial of it’s own. Which is not a bad idea for future tutorial :D

  23. sonia maklouf Avatar
    sonia maklouf

    Hi Denis, thanks for your tutorial.
    Can you take a look to http://stackoverflow.com/questions/38994626/ajax-load-more-posts-button ?

    Thanks

    1. Hi! I’ve replied to you on SO. Looks like the data that is returned is empty. Do you have posts in that category?

      1. sonia maklouf Avatar
        sonia maklouf

        I’ve six posts on test category and it seems to me that these category id is 25

        1. You can check the category id by hovering over them in the categories menu, and looking at the lower left corner, you should see a url that contains id of the category.

          1. sonia maklouf Avatar
            sonia maklouf

            i find exactly the same 25 id by hovering over them

          2. sonia maklouf Avatar
            sonia maklouf

            i don’t know what doesn’t work with cat so i change with category_name and it works perfectly

          3. But category name, you used name, not number, right? It should work (in theory). But if it’s working with name, that’s great as well :)

  24. Hi Denis, fantastic post. Just what I was looking for! Have a couple of questions that i’d really appreciate if you could answer for me. The additional posts are loading fine, but for some reason they are being duplicated, any idea why that might be? I had a look in the Networks tab and it seems each time I press the button, admin-ajax.php runs twice which causes this duplication.

    Also, as per your tutorial, by default it loads up normal ‘posts’ but what about custom posts? Would I need to duplicate the entire thing to get it to work for a custom post type on a separate page as well as the normal post type?

    Thanks,
    Rumman

    1. Looks like your script loads twice, you can try to use a trick to prevent double click. The script should work fine (I’m using it here on the front page). The script should work fine for custom posts but you’d need to modify the query to load them (and add the category query string to the script). I’m not on my computer so I cannot answer the post on so unfortunately.

      1. Thanks for the heads up, i’ll try and see if I can resolve it. Please take a look at the SO question whenever you get a chance. Thanks Denis!

        1. I think I located the issue. See on the SO for the answer :)

          1. Thanks Denis, I managed to figure it out but thanks for pointing me in the right direction. Turns out it was because I was loading my ‘main.js’ file twice – which contained the AJAX Code. Now i’ll try get this to work for custom posts, any help would again be much appreciated!

  25. Great work, Denis! I’m doing something wrong, it seems. I’ve attempted to do this on a fresh install of Twenty Sixteen to get it right but when I click the load more button it loads the entire index.php page again. Can’t figure where I erred so I thought I would ask… Thanks again!

    1. Post your code on SO and you can ping me there using `@` symbol with my name (dingo_d). If you followed the tutorial step by step it should be working :)

      1. Got it working correctly. I made the error of adding wp_localize_script twice, since twenty sixteen already had it.

  26. Nageena Akbar Avatar
    Nageena Akbar

    Hello
    I used your code but i have this error ReferenceError: ajax_posts is not defined
    pls tell me why i am facing this

    1. Can you give a bit more detail where this error happens? ReferenceError is usually a js error, did you define your ajax function the right way? In my case it’s called load_ajax_posts()

      1. Nageena Akbar Avatar
        Nageena Akbar

        i copied code from stackoverflow

        1. Did you localize your script using wp_localize_script? Read the article above and put the code in, and it should work ;)

          1. Nageena Akbar Avatar
            Nageena Akbar

            i have script.js in js folder

          2. Nageena Akbar Avatar
            Nageena Akbar

            wp_enqueue_script( ‘script_js’, get_template_directory_uri(). ‘/js/script.js’, array( ‘jquery’), ”, true ); wp_localize_script( ‘script_js’, ‘ajax_posts’, array( ‘ajaxurl’ => admin_url( ‘admin-ajax.php’ ), ‘noposts’ => __(‘No older posts found’, ‘gershman’), ));

            here i did

          3. Nageena Akbar Avatar
            Nageena Akbar

            http://gershman.wpengine.com/news/

            pls dear help me, i m in trouble

          4. Nageena Akbar Avatar
            Nageena Akbar

            can you help me dear?

          5. Why don’t you post a question on stackoverflow, it’s much easier that way. From your page it looks like you didn’t localize your script. Be sure to enqueue your script the right way: https://developer.wordpress.org/reference/functions/wp_enqueue_script/

          6. Nageena Akbar Avatar
            Nageena Akbar

            because of this problem i m in trouble, can we chat on skype so i can show ou my code, or i email you the code

          7. Nageena Akbar Avatar
            Nageena Akbar

            i am new with wp so i have no idea about localize the script

          8. Just post your code on http://stackoverflow.com/ And be as concise as possible, there are lots of examples how to do it. Plus if you read my article it explains it in detail.

          9. Nageena Akbar Avatar
            Nageena Akbar

            i cant put code on stackoverflow, there is some problem so i put them as txt on server, also sent links of each

          10. Nageena Akbar Avatar
            Nageena Akbar

            did you check my code files

          11. I’m sorry, but I cannot individually check every piece of code, it’s not suitable to answer here on comments. That’s why I asked you to post on SO. What kind of problem do your have on SO? The site is up and running…

          12. Nageena Akbar Avatar
            Nageena Akbar

            i put my code as comment in your post?

          13. Nageena Akbar Avatar
            Nageena Akbar

            I tried your code many times but i could not get the result.

          14. I replied to your post on SO

          15. You need to put the enqueue functions in a function that you’ll attach to a hook like this:

            
            
            /**
            
             * Proper way to enqueue scripts and styles.
            
             */
            
            function wpdocs_theme_name_scripts() {
            
                wp_enqueue_style( 'style-name', get_stylesheet_uri() );
            
                wp_enqueue_script( 'script-name', get_template_directory_uri() . '/js/example.js', array(), '1.0.0', true );
            
            }
            
            add_action( 'wp_enqueue_scripts', 'wpdocs_theme_name_scripts' );
            
            
            
          16. Nageena Akbar Avatar
            Nageena Akbar

            i did

            function nageen_enqueue_scripts(){

            wp_enqueue_script( ‘script_js’, get_template_directory_uri(). ‘/js/script.js’, array( ‘jquery’), ”, true );
            wp_localize_script( ‘script_js’, ‘ajax_posts’, array( ‘ajaxurl’ => admin_url( ‘admin-ajax.php’ ), ‘noposts’ => __(‘No older posts found’, ‘gershman’), ));

            }
            add_action( ‘wp_enqueue_scripts’, ‘nageen_enqueue_scripts’ );

            But now no error and no result

            http://gershman.wpengine.com/news/

          17. Nageena Akbar Avatar
            Nageena Akbar

            pls dear tell me,

  27. jose fano Avatar
    jose fano

    excelent tuto!! , where can I see a working demo of this?

    1. Hmmm, see I haven’t really given this a thought :D I’ll try to set up a demo site and link to it in the future. Thanks for the suggestion :)

      1. jose fano Avatar
        jose fano

        Yea but really this tuto is excellent, helped me a lot :)

        1. jose fano Avatar
          jose fano

          Do you see and advantage on using an external handler for the Ajax calls? I’ve seen a lot of people using that method too claiming that makes it faster.
          I was able to do my own Ajax calls with the guidance of ur tutorial and couple of others I found

          1. External handler as in admin-ajax.php? It’s hardly external, it’s a part of WordPress AJAX API, so it’s the best option actually :)

          2. jose fano Avatar
            jose fano

            Yea some people claim that using a different Ajax handler (not admin-ajax) can improve speed, because it would not include the whole wordpress environment, so it would be a light weight version. Although I’ve used that method I don’t see that much speed difference in my case, very minimal

          3. Oh, you’re not including the whole environment. That’s the thing. Before people would include the whole wp-load.php when calling ajax (because they just needed admin-ajax.php) like:

            include "../../../wp-load.php";

            This is a bad pracitce, and should never be used.
            But that’s why you pull only admin-ajax.php with your wp_localize_script, and you can use only it. I didn’t look into performance gains by using some outside handler vs the one that comes with WP, but I don’t think there should be a big difference. I’m all for the approach: use what you’re given :)

    2. Hi jose,

      I’ve set my front page to use the same code as above (with modifications to fit my posts style of course), so you can see how it’s working now :)

  28. Sven Parker Avatar
    Sven Parker

    Thanks for the help! One thing though, i had to declare $category_out=array(); in functions.php for my categories to work. Maybe add it into the code on the site or somebody might just see this comment and figure it out, anyway good luck!.

    1. Thanks for pointing that out :) I guess this was already declared for me and I missed it when I tested it out. Glad I could help ^^

      1. Nelson Avatar
        Nelson

        Hi Denis, is very good you tutorial, is different. sorry about this, can you please give you comment in this post in stackoverflow:
        http://stackoverflow.com/questions/37253810/ajax-request-repeat-the-same-value-from-php

        I’m going to thank a lot

        1. I answered your post, and tested it so it works now ;)

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.