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>',
'“' . get_search_query() . '”'
);
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.
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
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!
Leave a Reply