Skip to content

WordPress custom posts archive with REST API and ajax

WordPress Rest API and Ajax

Set up initial variables #

Go to workflow

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 #

Go to workflow

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 #

Go to workflow

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 pageFailure ends here, please add the following code for the callback of categories.

/**
 * @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.


Pages: 1 2 3 4

Leave a Reply