0

I have a page on a current project where I've successfully set up AJAX post filtering, so the user can click on a checkbox and filter the posts by the category the corresponds to it. I've also mostly successfully set up AJAX post loading where the user scrolls to the bottom and more posts are loaded in. I'll describe my problem and then post my code.

My Problem

When the new posts load in on scroll by AJAX, they aren't in the right category (or categories, depending on which checkboxes are clicked). Also, I have it set to load in 9 new posts at a time, and they're just repeating the same 9 posts every time.

My Code

In my functions.php file, I have the following code:

    // Setup AJAX for filter posts by category and for load more button
    function ajax_filter_posts_scripts() {
        // Enqueue script
        wp_register_script('afp_script', get_stylesheet_directory_uri() . '/js/ajax-filter-posts.js', false, null, false);
        wp_enqueue_script('afp_script');

        global $wp_query;
        wp_localize_script( 'afp_script', 'afp_vars', array(
                    'afp_ajax_url' => admin_url( 'admin-ajax.php' ),
                    'posts' => json_encode( $wp_query->query_vars ), // everything about your loop is here
                    'current_page' => get_query_var( 'paged' ) ? get_query_var('paged') : 1,
                    'max_page' => $wp_query->max_num_pages
                )
        );
    }
    add_action('wp_enqueue_scripts', 'ajax_filter_posts_scripts', 100);

    // Filter posts by category
    function prefix_load_cat_posts () {

            $cat_id = $_POST['category__in'];

                $args = array (
                    'post_type' => 'post',
                    'post_status' => 'publish',
                    'category__in' => $cat_id,
                    'posts_per_page' => 9,
                    'orderby' => 'date',
                    'order' => 'DESC',
                );

            $filter_posts = new WP_Query($args);
            ob_start ();

            if ( $filter_posts->have_posts() ) : while ( $filter_posts->have_posts() ) : $filter_posts->the_post(); ?>

                <article class="article-card">
                    <header class="card-header">
                        <div class="card-img" style="background: url('<?php echo the_post_thumbnail_url( 'small' ); ?>') no-repeat center center; width: 100%; background-size: cover;"></div>
                        <?php the_title( "<h4 class='article-title'>", "</h4>" ) ?>
                        <p class="article-author">By
                            <?php the_author(); ?>
                        </p>
                    </header>
                    <div class="card-content">
                        <p>
                            <?php echo wp_trim_words(get_post_meta(get_the_ID(), '_yoast_wpseo_metadesc', true), 15, '...'); ?>
                        </p>
                    </div>
                    <footer class="card-footer">
                        <a href="<?php the_permalink(); ?>" class="btn btn-article" role="button">Read Now</a>
                    </footer>
                </article>

                <?php $post_count = $filter_posts->current_post + 1; ?>
                <?php if ($post_count % 3 == 0): ?>

                <div class="ad-wrapper">
                    <?php the_ad(202070); ?>
                </div>

                <?php
                    endif;
                    endwhile; endif;

            wp_reset_postdata();

        $response = ob_get_contents();
        ob_end_clean();

        echo $response;

        die(1);
    }
    add_action( 'wp_ajax_nopriv_load-filter', 'prefix_load_cat_posts' );
    add_action( 'wp_ajax_load-filter', 'prefix_load_cat_posts' );

    // AJAX Load More
    function afp_load_more() {
        $args = isset( $_POST['query'] ) ? array_map( 'esc_attr', $_POST['query'] ) : array();
        $args['post_type'] = isset( $args['post_type'] ) ? esc_attr( $args['post_type'] ) : 'post';
        $args['paged'] = esc_attr( $_POST['page'] );
        $args['post_status'] = 'publish';
        $args['category__in'] = $cat_id;
        $args['order'] = 'DESC';
        $args['orderby'] = 'date';
        $args['offset'] = 9;
        $args['posts_per_page'] = 9;
        var_dump($args);
        ob_start();
        $loop = new WP_Query( $args );
        if( $loop->have_posts() ): while( $loop->have_posts() ): $loop->the_post();
        ?>

        <article class="article-card">
            <header class="card-header">
                <div class="card-img" style="background: url('<?php echo the_post_thumbnail_url( 'small' ); ?>') no-repeat center center; width: 100%; background-size: cover;"></div>
                <?php the_title( "<h4 class='article-title'>", "</h4>" ) ?>
                <p class="article-author">By
                    <?php the_author(); ?>
                </p>
            </header>
            <div class="card-content">
                <p>
                    <?php echo wp_trim_words(get_post_meta(get_the_ID(), '_yoast_wpseo_metadesc', true), 15, '...'); ?>
                </p>
                <p><?php $category = get_the_category(); echo $category[0]->cat_name; ?></p>
            </div>
            <footer class="card-footer">
                <a href="<?php the_permalink(); ?>" class="btn btn-article" role="button">Read Now</a>
            </footer>
        </article>

        <?php $post_count = $loop->current_post + 1; ?>
        <?php if ($post_count % 3 == 0): ?>

        <div class="ad-wrapper">
            <?php the_ad(202070); ?>
        </div>

        <?php
        endif;
        endwhile; endif; wp_reset_postdata();
        $res = ob_get_contents();
        ob_end_clean();

        echo $res;
        wp_die();
    }
    add_action( 'wp_ajax_afp_load_more', 'afp_load_more' );
    add_action( 'wp_ajax_nopriv_afp_load_more', 'afp_load_more' );

In my JavaScript file, I have this code:

// AJAX Post Filter scripts
var $checkbox = $("#filter input:checkbox");

let categoryIDs = [];

$checkbox.change((e) => {
  let value = Number(e.currentTarget.value);

  if ($checkbox.is(':checked')) {
    categoryIDs.indexOf(value) === -1 ? (
      categoryIDs.push(value)
    ) : (
      categoryIDs = categoryIDs.filter((item) => item !== value)
    )
  } else {
    categoryIDs = [3, 4, 28, 35, 353];
    // TODO: move this to the functions.php file if possible? if/else to reset var to initial array if AJAX returns empty?
  }

  categoryIDs.forEach((item) => {
    $.ajax({
      type: 'POST',
      url: afp_vars.afp_ajax_url,
      data: {
        action: "load-filter",
        category__in: categoryIDs
      },
      success: function (response) {
        $(".filter-section").empty().html(response);
        return false;
      }
    })
  });
});

// AJAX Load More Posts scripts
// TODO: Fix this.
var canBeLoaded = true;
var bottomOffset = 1200;
var page = 2;
var loading = false;
var scrollHandling = {
  allow: true,
  reallow: function () {
    scrollHandling.allow = true;
  },
  delay: 400
};

$(window).scroll(function () {
  if (!loading && scrollHandling.allow) {
    scrollHandling.allow = false;
    setTimeout(scrollHandling.reallow, scrollHandling.delay);
    if ($(document).scrollTop() > ($(document).height() - bottomOffset) && canBeLoaded == true) {
      $.ajax({
        type: 'POST',
        url: afp_vars.afp_ajax_url,
        data: {
          action: "afp_load_more",
          page: page,
          query: afp_vars.query
        },
        success: function (res) {
          $(".filter-section").append(res);
          page += 1;
        }
      })
    }
  }
});

And lastly, I have this code in my template file:

      <!-- Filter Area -->
      <div class="section">
        <div class="container">
          <h4 class="ctr">Choose a Filter Below</h4>
          <div class="article-filters">
          <form id="filter">
            <?php
              $args = array(
                'include' => array(3, 4, 28, 35, 353),
                'orderby' => 'name',
                'order' => 'ASC',
                'posts_per_page' => -1
              );

              $categories = get_categories($args);
                foreach( $categories as $cat ) {
                  $cat_id = get_cat_ID( $cat->name );

                  echo '<span>
                          <input class="checkbox-custom" type="checkbox" id="check'. $cat_id .'" name="category-checks" value="' . $cat_id . '">
                          <label class="checkbox-custom-label" for="check'. $cat_id .'">' . $cat->name . '</label>
                        </span>';
                }

                wp_reset_postdata();
            ?>
          </form>
          </div>
          <div class="filter-section">
            <?php
              $args = array(
                'post_type' => 'post',
                'post_status' => 'publish',
                'category__in' => array(3, 4, 28, 35, 353),
                'posts_per_page' => 9,
                'orderby' => 'date',
                'order' => 'DESC'
              );

              $filter_posts = new WP_Query($args);
              if ( $filter_posts->have_posts() ) : while ( $filter_posts->have_posts() ) : $filter_posts->the_post();
            ?>

            <article class="article-card">
              <header class="card-header">
                <div class="card-img" style="background: url('<?php echo the_post_thumbnail_url( 'small' ); ?>') no-repeat center center; width: 100%; background-size: cover;"></div>
                <?php the_title( "<h4 class='article-title'>", "</h4>" ) ?>
                <p class="article-author">By <?php the_author(); ?></p>
              </header>
              <div class="card-content">
                <p><?php echo wp_trim_words(get_post_meta(get_the_ID(), '_yoast_wpseo_metadesc', true), 15, '...'); ?></p>
              </div>
              <footer class="card-footer">
                <a href="<?php the_permalink(); ?>" class="btn btn-article" role="button">Read Now</a>
              </footer>
            </article>

            <?php $post_count = $filter_posts->current_post + 1; ?>
            <?php if ($post_count % 3 == 0): ?>

            <div class="ad-wrapper">
               <?php the_ad(202070); ?>
            </div>

            <?php
              endif;
              endwhile; endif;

              wp_reset_query();

            ?>

          </div>
        </div>
      </div>

I used two or three different guides to patch this all together, so I'm sure I've made an error somewhere. Unfortunately, I can't figure out where. I don't expect anyone to do my work for me, but if you could help me find the problem, I'd be super grateful!

  • have you checked your categoryIDs var in ajax whether it is giving you correct IDs or not? – Piyush Rawat Nov 29 '17 at 6:02
  • Your afp_load_more() function is setting $args['category__in'] to $cat_id, but I don't see $cat_id being set anywhere in that function. – Jacob Peattie Nov 29 '17 at 6:06
  • It does return correctly in my first function for the post filtering. I don't know how to get that value to the other function though. – KreigD Nov 29 '17 at 6:07
  • Sorry that was from some code that didn't work which I deleted. I forgot to remove that line, too. – KreigD Nov 29 '17 at 6:07
  • 1
    try like this pastebin.com/9P2LBWiu – Piyush Rawat Nov 29 '17 at 7:17
0

I solved the issues this way:

In my functions.php file, I added the following code:

if (empty($cat_id)) {
    $cat_id = array(3, 4, 28, 35, 353);
}

In my second AJAX function like so:

function afp_load_more() {
  $cat_id = $_POST['category__in'];
  if (empty($cat_id)) {
    $cat_id = array(3, 4, 28, 35, 353);
  }
...etc

So that when the categoryIDs from my JavaScript returned an empty array (before a user clicks any checkboxes), it would default to the five categories chosen for the checkbox filters.

In my JavaScript, I didn't have to change much as I discoved the categoryIDs variable was usable by my second AJAX call as-is. The only change I made for that issues was to add category__in: categoryIDs to my second AJAX call under the data array.

For the issue of the repeating posts, I realized I just needed to set up a primative counter to add the value of the posts_per_page to the offset parameter in my query.

Here's my finished, working JavaScript for the load-on-scroll AJAX call (changes are noted in comments):

// AJAX Load More Posts scripts
var canBeLoaded = true;
var bottomOffset = 1500;
var page = 2;
var postOffset = 9; // Added a variable for the offset
var loading = false;
var scrollHandling = {
  allow: true,
  reallow: function () {
    scrollHandling.allow = true;
  },
  delay: 400
};

$(window).scroll(function () {

  if (!loading && scrollHandling.allow) {
    scrollHandling.allow = false;
    setTimeout(scrollHandling.reallow, scrollHandling.delay);
    if ($(document).scrollTop() > ($(document).height() - bottomOffset) && canBeLoaded == true) {
      loading = true; // This is important but I forgot it last time around
      $.ajax({
        type: 'POST',
        url: afp_vars.afp_ajax_url,
        data: {
          action: "afp_load_more",
          page: page,
          query: afp_vars.query,
          category__in: categoryIDs, // Added the variable from the first AJAX call here as-is because it's working now
          offset: postOffset // Added the offset parameter with the variable to give it the value
        },
        success: function (res) {
          $(".filter-section").append(res);
          page += 1;
          postOffset += 9; // Added a counter to add 9 to the offset on success because I load in 9 posts every time the AJAX call runs
          loading = false; // This is important, but I forgot it last time around
        }
      })
    }
  }
});

Oh, and don't forget (like I did) to go back and use your data for the offset parameter from the AJAX call into the functions.php file (in the second AJAX function, with the rest of the $args). Like so:

$args['offset'] = esc_attr( $_POST['offset'] );

Hope this helps somebody else. And if anyone has a critique of my code that could make it better, please add your input!

  • Nice to see you sorted it out!! – Piyush Rawat Nov 29 '17 at 9:15

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.