0

The website has a blog page with a search field and category checkboxes. Searching for something or checking/unchecking a category starts an AJAX request to get the posts based on the search/categories, then populates those results into the list on the page.

Currently this functionality works with jQuery, but my attempts to write it in plain JS are failing with 400 errors.

Here is the jQuery for this that works:

$.ajax({
    url: Knuckle.ajaxurl,
    type: 'post',
    dataType : 'json',
    data: {
        action: 'kp_filters',
        nonce: Knuckle.nonce,
        form: serialized_form,
        base_url: base_url
    },
    success: function( response ){
        // console.log('===== SUCCESS =====');
        // console.log(response);

        if( response.results && response.results.length ){
            $('.results-item').remove();
            $('.results-pagination').remove();

            for(i=0; i<response.results.length; i++){
                $('.results-list').append( response.results[i] );
            }

            if( response.pagination && response.pagination.length ){
                $('.results-inner').append( response.pagination );
            }

        } else {
            // No results.
            $('.results-item').remove();
            $('.results-pagination').remove();
        }

    },
    error: function( response ){
        // console.log('===== ERROR =====');
        // console.log(response);
    },
    complete: function( response ){
        // console.log('===== COMPLETE =====');
        // console.log(response);

        // Remove the loading indicator
        $('.sk-circle').removeClass('is-loading');

    }
});

Here's the plain Javascript version of the above snippet, which is getting the 400 errors:

var serialized_form = encodeURI( kp_serialize(window.mirrored_params) );
var base_url = kp_get_base_url();

var data = {
    url: Knuckle.ajaxurl,
    type: 'post',
    dataType : 'json',
    action: 'kp_filters',
    nonce: Knuckle.nonce,
    form: serialized_form,
    base_url: base_url
};
console.log('About to post this data: ');
console.log(data);

var xhr = new XMLHttpRequest();
xhr.open('post', Knuckle.ajaxurl);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

xhr.onreadystatechange = function() {
    var DONE = 4; // readyState 4 means the request is done.
    var OK = 200; // status 200 is a successful return.
    if( xhr.readyState === DONE ){
        console.log('ajax is done: ' + xhr.status);
        if( xhr.status === OK ){
            console.log(xhr.responseText); // 'This is the returned text.'
        }
    } else {
        console.log('Error: ' + xhr.status); // An error occurred during the request.
    }
};

xhr.send(data);

Here's a screenshot of what I get in the console: https://imgur.com/a/y8ULe

I've been messing with the data and trying different headers for my XMLHttpRequest, but it always comes back with a 400. Tried disabling plugins that might have an effect on it, but nothing there either.

What am I doing wrong with the plain JS AJAX? What is jQuery doing that my code isn't?

  • 2
    Convert data to a string, i.e.: data = 'action=kp_filters&nonce=' + Knuckle.nonce + '&form=' + serialized_form + '&base_url=' + base_url;. And secondly, the request header should be application/x-www-form-urlencoded. See this and this. – Sally CJ Mar 14 '18 at 2:55
  • 1
    That was it, thank you very much! I serialized the data and added the different header and it worked perfectly :) – James Mar 14 '18 at 18:14
1

Thank you Sally CJ for helping me out! The problem was that the data needs to be sent as a serialized string (query string) and my request header was wrong.

Here is the final working function I have now. Sorry about the extraneous stuff about the loading animation and the scrolling to the top of the results list. Hopefully this helps someone in the future.

function kp_submit_filters(){
// console.log( 'START kp_submit_filters()' );
// console.log( window.mirrored_params );

// Start the loading animation.
var loading_indicator = document.querySelectorAll('.sk-circle');
if( loading_indicator.length ){
    kpAddClass(loading_indicator, 'is-loading');
}

// Scroll to the top of the results.
var target = document.querySelectorAll('.results');
if (target.length) {
    var target_shape = target[0].getBoundingClientRect();
    var target_position = target_shape.top + window.pageYOffset;
    window.scroll({
        top: target_position - 60,
        left: 0,
        behavior: 'smooth'
    });

}


// Set up some data to send in the AJAX request.
var base_url = kp_get_base_url();

var data = window.mirrored_params;
    data.action = 'kp_filters';
    data.nonce = Knuckle.nonce;
    data.base_url = base_url;

// Serialize and encode the data object.
var serialized_data = encodeURI( kp_serialize(data) );

// console.log('About to post this data: ');
// console.log( data );
// console.log( serialized_data );

// Make the request object.
var xhr = new XMLHttpRequest();

// Set up what happens with the response - remove existing result items and pagination, add the new search result items and pagination to the DOM, and also stop the loading animation.

xhr.onreadystatechange = function() {
    var DONE = 4; // readyState 4 means the request is done.
    var OK = 200; // status 200 is a successful return.
    if( xhr.readyState === DONE ){
        // console.log('ajax is done: ' + xhr.status);
        if( xhr.status === OK ){
            // console.log('status is OK');
            // console.log(xhr);
            // console.log(xhr.responseText); // 'This is the returned text.'

            var response = JSON.parse( xhr.responseText );
            var result_items = document.querySelectorAll('.results-item');
            var result_pagination = document.querySelectorAll('.results-pagination');
            var results_list = document.querySelectorAll('.results-list');

            if( response.results && response.results.length ){

                // Remove the existing items.
                for( i=0; i<result_items.length; i++){
                    result_items[i].parentNode.removeChild(result_items[i]);
                }

                // Remove the existing pagination.
                for( i=0; i<result_pagination.length; i++){
                    result_pagination[i].parentNode.removeChild(result_pagination[i]);
                }

                // Add the new items and pagination to the DOM.
                if( results_list.length ){
                    for(i=0; i<response.results.length; i++){
                        results_list[0].innerHTML += response.results[i];
                    }

                    if( response.pagination && response.pagination.length ){
                        results_list[0].innerHTML += response.pagination;
                    }

                }



            } else {
                // No results, so just remove the existing items and pagination.

                for( i=0; i<result_items.length; i++){
                    result_items[i].parentNode.removeChild(result_items[i]);
                }

                for( i=0; i<result_pagination.length; i++){
                    result_pagination[i].parentNode.removeChild(result_pagination[i]);
                }

            }

        }

        // Hide the loading animation.
        if( loading_indicator.length ){
            kpRemoveClass(loading_indicator, 'is-loading');
        }

    } else {
        // console.log('Error: ' + xhr.status); // An error occurred during the request.
    }

};

// Send the POST request via AJAX.
xhr.open('POST', Knuckle.ajaxurl, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
xhr.send( serialized_data );

return false;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.