Change AJAX filter from POST to GET for URL Parameters

I would divide your question into two parts:

  1. Use the query parameters as default for the filter on page load.
  2. Update the query parameters dynamically when the form values change.

To achieve 1. you’ll first need to extract the logic to build $args in property_filters() so it can be used on the initial load:

function prepare_property_filters(array $args): array {
    if (!empty($_REQUEST['price_min']) || !empty($_REQUEST['price_max'])) {
        $args['meta_query'] = ['relation'=>'AND'];
    }
    // etc.
    return $args;
}

I’ve made some additional changes to your code:

  • Add type declarations.
  • Change isset($foo) && $foo to !empty($foo) which is basically the same*.
  • Use short array syntax.
  • Switch from $_POST to $_REQUEST, the benefit here is that you don’t have to differentiate between $_GET and $_POST.

You can use this then like so

functions.php

function property_filters() {
    $order = explode( '-', $_POST['sort_by_dropdown'] );

    $args = prepare_property_filters([
        'posts_per_page' => 9, 
        'post_status' => is_user_logged_in() ? ['publish', 'private'] : ['publish'],
        'paged' => $_POST['page']
    ]);
    query_posts( $args );

    global $wp_query;
    if( have_posts() ) :
       // etc.

HTML

<ul id="main_posts" class="item-listings">
<?php 
// Build the inital Loop
$args = prepare_property_filters([
    'posts_per_page' => 9,
    'paged' => $paged
]);

query_posts($args);
// etc.

Now on the initial page load and with the query parameters, you should see the list properly filtered.

But the user will not have these values pre-selected, so update the form HTML like so

<?php
$price_min = [
    '' => 'Any Price',
    '100000' => '$100,000',
    //etc.
];
?>
<select name="price_min" class="min_max_select">
    <option disabled="disabled" selected="selected" value="">Minimum Price</option>
     <?php
     foreach ($price_min as $key => $value) {
         $selected = '';
         if (!empty($_GET[$key]) && $_GET[$key] === $value) {
             $selected = ' selected="selected"';
         }
         printf(
             '<option value="%s"%s>%s</option>',
             $key,
             $selected,
             $value
         );
     }
     ?>
</select>

Now we truly solved problem #1. If the query parameters are present, the list will be pre-filtered and the proper filters will already be selected in the form.


As for problem #2, this can be boiled down to “change to new URL without reloading”, which already has great answers like this one. The idea usually is to use window.history.pushState(). (Check the MDN docs for the History API for a depper look into this.)

$('#filter').change(function(){

    $.ajax({
        url : prop_loadmore_params.ajaxurl,
        data : $('#filter').serialize(), 
        dataType : 'json',
        type : 'POST',
        beforeSend : function(xhr){
            $('#filter').find('button').text('Filtering...');
        },
        success : function( data ){
            // set HTML, variables, etc. as you currently do

            window.history.pushState($('#filter'), 'title', '?' + $('#filter').serialize());
        }
    });

    return false;
});

I’m not entirely sure, if title is used (second argument), you can probably pass it via localize as well.

This should change the URL to reflect the changed form. And this solves problem #2.

*technically, this is not true but in my experience, in most use cases the edge cases don’t matter