Set up initial variables #
Now that we have basic understanding on the JSON data, we can start our jQuery. Open our api-posts-archive.js in a text editor and add the following code to it.
(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
}
// Initialize on page load (front end).
$(document).ready(function(){
initializePosts( $(this) )
})
})(jQuery)
The getJSON method #
We have set our variables for different use. As the code is commented well, you can see which variable is for what purpose. At this point, we can set our initial page data with getJSON method. We will be using when-then method for this. Please add the following code just below this line – //add our posts and categories for initial page load
//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 )
The .then()
method will accept 2 functions. pageCallback
function will return the JSON data in a array for both API url that we have set in the .when()
method. The second function pageFailure
will be called if any of our API url gets an error. Now we can add our both callback functions. Just add the following code below the above code.
/**
* @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
If you read the code, I have added relevant comments to it. The callback function pageCallback()
accepts 2 parameters for categories and posts. Then we have used those parameters to fetch real data. You can see, we have stored the category links inside a variable called allCategories
. Because, from now on we can use our category links and fetch posts for each of them. The event listener function getPostsByCategory()
will do that.
Similarly, after the initial posts are fetched, we have initialized the Load More button click event. You can see the code below this line – //initialize load more posts click event
. The callback function for this is loadNextPage()
.
Work with different click events for categories and load more #
At this point, we are ready to start our click event functions for categories and load more button. We shall accomplish it one by one.
1. The callback for the categories click events
Just below this line – //callback
, please add the following code for the callback of categories.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
I have used comments for everything for better understanding. Basically this function will be called each time the category links are clicked. It will fetch posts by category as you can see the API request suggests that.
2. The callback for the load more click event
Just below this line //callback getPostsByCategory ends here
, please add the code for load more button event.
/**
* @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
Same as before, you can check the comments on the code for more understanding. This function will be called whenever the load more button is clicked. But it will also check some conditions. In 2 scenarios, this event can happen. One is when the initial page is loaded, second is when category links are clicked and need to load next page by category.