<div class="container pt-5">
<h1>WordPress posts archive with REST API - A practical example</h1>
<a href="https://templateartist.com/2020/05/12/wordpress-custom-posts-archive-with-rest-api-and-ajax/" >Tutorial for WordPress posts archive with REST API and ajax</a>
<hr>
<div class="api-grid my-5">
<aside class="post-categories">
<div class="categories-container">
<h3>Categories</h3>
<ul id="categories">
<div class="text-center">
<div class="spinner-border m-3" role="status"></div>
<p>Loading...</p>
</div>
</ul>
</div>
</aside>
<section class="posts-wrap">
<h3>Articles</h3>
<div id="all-posts" class="all-posts" data-per-page="4">
<div class="text-center">
<div class="spinner-border m-3" role="status"></div>
<p>Loading...</p>
</div>
</div>
<button id="load-more" class="load-more btn btn-primary end-page">Load More</button>
</section>
</div>
</div>
.api-grid {
display: flex;
flex-direction: column;
}
.post-categories,
.posts-wrap {
margin-top: 30px;
}
.post-categories ul{
padding: 0;
margin: 0;
}
.post-categories ul li{
list-style: none;
margin-bottom: 10px;
}
.post-categories ul li a{
display: block;
padding: 10px 20px;
background-color: #dddddd;
color: #333333;
transition: all ease 0.3s;
}
.post-categories ul li a:hover,
.post-categories ul li a.current
{
background-color: #333333;
color: #dddddd;
text-decoration: none;
}
.load-more{
margin-top: 30px;
}
.load-more.end-page{
opacity: 0.3;
pointer-events: none;
}
.new-post{
opacity: 0;
}
.new-post.fadein{
opacity: 1;
transition: all ease 0.3s;
}
@media (min-width:600px){
.api-grid {
flex-direction: row;
}
.post-categories{
width: 30%;
position: relative;
}
.categories-container{
position: sticky;
top: 50px;
padding-right: 40px;
}
.posts-wrap{
width: 70%;
}
}
.all-posts article{
background-color: #f8f9fa;
padding: 15px;
margin-bottom: 30px;
}
@media (min-width:800px){
.all-posts article{
display: flex;
}
.all-posts article .post-title-excerpt{
width: 100%;
padding: 15px;
}
}
@media (min-width: 992px){
.container{
max-width: 940px;
}
}
@media (min-width: 992px){
.container{
max-width: 960px;
}
}
(function($){
//always use strict mode
"use strict"
let initializePosts = () => {
/**
* All variables
*/
//posts container for append or replace data
let container = $('#all-posts')
//categories container
let catContainer = $('#categories')
//load more button
let loadMore = document.querySelector('#load-more')
//set initial current page
let currentPage = 1;
//set initial category ID to blank
let categoryID = ''
//set initial total pages to blank
let totalPages = ''
//get posts per page from the data attribute
let perPage = container.data('per-page')
//site url
let siteurl = encodeURI('https://demo.wp-api.org/wp-json')
//add our posts and categories for initial page load
$.when(
$.getJSON( siteurl + '/wp/v2/categories' ),
$.getJSON( siteurl + '/wp/v2/posts?per_page=' + perPage )
).then( pageCallback, pageFailure )
/**
* @callback pageCallback
* @param categories for category terms
* @param posts for posts
*/
function pageCallback( categories, posts ){
//fetch and return our post categories
if( categories[0].length ){
console.log(categories[0])
catContainer.html(
categories[0].map( cat => {
if( cat.count != 0 ){
return `<li><a class="post-category" href="#" data-term-id="${cat.id}">${cat.name}</a></li>`
}
}).join('')
)
//store our category links for click event function
let allCategories = $('.post-category')
let i;
//initialize category links click event if condition is a match
for( i = 0; i < allCategories.length; i++ ) {
allCategories[i].addEventListener('click', getPostsByCategory )
//the callback `getPostsByCategory` is written later in the code
}
}
//now, fetch and return the post data in our page
if( posts[0].length ){
console.log(posts)
//store the initial total pages available for the query
totalPages = parseInt(posts[2].getResponseHeader('X-WP-TotalPages'))
container.html(
posts[0].map( post => {
return `
<article>
<div class="post-title-excerpt">
<h3><a href="${post.link}">${post.title.rendered}</a></h3>
${post.excerpt.rendered}
</div>
</article>
`
}).join('')
).hide().fadeIn(400)
//initialize load more posts click event
//callback function `loadNextPage` is written later in the code
loadMore.addEventListener('click', loadNextPage)
//if there are more than 1 page,
//remove the 'end-page' class from load more button
if( currentPage < totalPages){
loadMore.classList.remove('end-page')
}
}
}//callback `pageCallback` ends here
/**
* @callback pageFailure
* call this if our API url gets error
*/
function pageFailure(){
console.log("JSON Error for posts!")
}//callback `pageFailure` ends here
/**
* @callback getPostsByCategory
* load posts when category terms are clicked
*/
function getPostsByCategory(e){
e.preventDefault()
//get the term id from the HTML data attribute
let term_id = $(this).data('term-id')
//this condition checks if the clicked link is already active or not
//if clicked link not active, run this
if( !$(this).hasClass('current') ){
//API request
$.getJSON( siteurl + '/wp/v2/posts?per_page=' + perPage + '&categories=' + term_id, ( posts, status, response ) => {
//now setting the global category ID for the load more use
categoryID = term_id
//setting the global total pages for load more use
totalPages = response.getResponseHeader('X-WP-TotalPages')
//setting the current page to first page again
currentPage = 1
//checking if there are more than 1 pages
if( currentPage == totalPages){
//if first page is also the last page,
//add 'end-page' class to load more button
loadMore.classList.add('end-page')
}else{
//if there are more than 1 page,
//remove the 'end-page' class from load more button
loadMore.classList.remove('end-page')
}
//getting data from the API request
if( posts.length ){
container.html(
posts.map( post => {
return `
<article>
<div class="post-title-excerpt">
<h3><a href="${post.link}">${post.title.rendered}</a></h3>
${post.excerpt.rendered}
</div>
</article>
`
}).join('')
).hide().fadeIn(400)
}
})
//get all .current items
let current = $(".current");
//remove class .current from all items
current.removeClass('current')
//set .current class to the clicked item
$(this).addClass('current')
}
}//callback `getPostsByCategory` ends here
/**
* @callback loadNextPage
* load next page function
*/
function loadNextPage(e){
e.preventDefault()
//run this if category links are clicked
if( categoryID != '' && currentPage < totalPages ){
//increment current page number
currentPage = currentPage + 1;
//API request
$.getJSON( siteurl + '/wp/v2/posts?per_page=' + perPage + '&categories=' + parseInt(categoryID) + '&page=' + currentPage, data => {
//getting data from the API request
if(data.length){
container.append(
data.map( post => {
return `
<article class="new-post">
<div class="post-title-excerpt">
<h3><a href="${post.link}">${post.title.rendered}</a></h3>
${post.excerpt.rendered}
</div>
</article>
`
}).join('')
)
}
//adding a class(for fade in effect) to the newly added posts
window.setTimeout( () => $("#all-posts .new-post").addClass('fadein'), 100)
})
//adding 'end-page' class to the loadMore button if there is no more pages
if( currentPage == totalPages ){
$(this).addClass('end-page')
}
console.log(currentPage)
} else if( currentPage < totalPages ) {
//run this to fetch all posts regardless of categories
//(after inital page load when no category link is clicked)
currentPage = currentPage + 1;
$.getJSON( siteurl + '/wp/v2/posts?per_page=' + perPage + '&page=' + currentPage , data => {
//getting data from the API request
if(data.length){
container.append(
data.map( post => {
return `
<article class="new-post">
<div class="post-title-excerpt">
<h3><a href="${post.link}">${post.title.rendered}</a></h3>
${post.excerpt.rendered}
</div>
</article>
`
}).join('')
)
}
//adding a class(for fade in effect) to the newly added posts
window.setTimeout( () => $("#all-posts .new-post").addClass('fadein'), 100)
})
//adding a class to the loadMore button if there is no more pages
if( currentPage == totalPages ){
$(this).addClass('end-page')
}
}
}//callback `loadNextPage` ends here
}
// Initialize on page load (front end).
$(document).ready(function(){
initializePosts( $(this) )
})
})(jQuery)