1
\$\begingroup\$

In my WordPress theme I have built a function that uses Isotope Layout by Metafizzy to accomplish filtering and sorting.

I have used a mix of AJAX and JQuery to accomplish this, however I feel the code is very repetitive and potentially overly long.

/**
 * Get posts using AJAX
 */
jQuery(document).ready(function ($) {

  /**
   * All filter inputs
   */
  let $filters = $(".filters input");

  /**
   * The selectable categories
   */
  let $categories = $(".filter-box__categories input");

  /**
   * The selectable tags
   */
  let $tags = $(".filter-box__categories input");

  /**
   * Initialize the load more
   */
  let load_more = $("button.load-more-posts");

  /**
   * The area to show result counts
   */
  let $resultCount = $(".filter-box__result-count");

  /**
   * An array to store selected filters
   */
  let $selectedFilters = [];
  let $selectedCategories = [];
  let $selectedTags = [];

  /**
   * Declare that the load more button has not been pressed
   */
  let has_run = false;

  /**
   * Specify that the initial next page is always the second
   */
  let next_page = 2;

  /**
   * Initialize the Isotope grid
   */
  const $grid = $("#grid").isotope({
    itemSelector: ".grid-item",
    percentagePosition: true,
    animationEngine: "best-available",
    animationOptions: {
      duration: 800
    },
    filter: "*",
    masonry: {
      columnWidth: ".grid-item",
      gutter: 30
    }
  });

  /**
   * Get the Isotope instance
   */
  const $items = $grid.data("isotope");

  /**
   * Each time an image is loaded, re-layout the grid.
   * This prevents any weird overlapping.
   */
  $grid.imagesLoaded().progress(function () {
    $grid.isotope("layout");
  });

  /**
   * Apply filters to Isotope when any filters are updated.
   * This will also update the result count and add some block elements.
   */
  $filters.change(function () {

    has_run = false;

    $selectedCategories = [];
    $selectedTags = [];
    $selectedFilters = [];

    // Loop through filters and add values if checked
    $(".filters input").each(function (counter, element) {
      if (element.checked) {
        $selectedFilters.push(element.value);
      }
    });

    // If there are filters in the filters array, join them
    if ($selectedFilters.length > 0) {
      $(".filter-box__applied-filters-area").show();
      $filters = $selectedFilters.join(", ");
    } else {
      $filters = "*";
    }

    // Add categories to their own array
    $(".filter-box__categories input").each(function (counter, element) {
      if (element.checked) {
        $selectedCategories.push(element.value);
      }
    });

    if ($selectedCategories.length > 0) {
      $categories = $selectedCategories.join(", ");
      $('.toggler--category .toggle').text("Category " + "(" + $selectedCategories.length + ")");
    } else {
      $('.toggler--category .toggle').text("Category");
    }

    // Add tags to their own array
    $(".filter-box__tags input").each(function (counter, element) {
      if (element.checked) {
        $selectedTags.push(element.value);
      }
    });

    if ($selectedTags.length > 0) {
      $tags = $selectedTags.join(", ");
      $('.toggler--tag .toggle').text("Tag " + "(" + $selectedTags.length + ")");
    } else {
      $('.toggler--tag .toggle').text("Tag");
    }

    // Send AJAX request to get posts relating to filter selections
    $.ajax({
      type: "POST",
      dataType: "json",
      url: ajax_get_posts_object.ajax_url,
      data: {
        action: "ajax_get_posts",
        security: ajax_get_posts_object.ajax_nonce,
        categories: $selectedCategories,
        tags: $selectedTags,
      },
      beforeSend: function (xhr) {
        //
      },
      success: function (response) {
        if (response) {
          console.log(response);
          // An array to store new items added via AJAX
          let new_items = [];

          // Run through JSON
          $.each(response.posts, function (key, value) {
            let $new_item = $(`<div class="grid-item article-post-card ${value.category[0]["slug"]} ${value.tags ? value.tags[0]["slug"] : ''}">
              <a class="article-post-card__anchor" href=" ${value.permalink}" alt="${value.title}">
                    <div class="article-post-card__featured-image-container">
                      <div class="article-post-card__overlay"></div>
                      <div class="article-post-card__featured-image">
                          ${value.thumbnail}
                      </div>

                      <div class="article-post-card__category-label">
                          ${value.category[0]["name"]}
                      </div>

                  </div>

                    <div class="article-post-card__content-wrapper">

                      <div class="article-post-card__publish-date">
                          <time class="updated" datetime="${value.post_time}">${value.date}</time>
                      </div>

                      <div class="article-post-card__title">
                          ${value.title}
                      </div>

                      <div class="article-post-card__excerpt">
                        ${value.introduction}
                      </div>

                  </div>
              </a>
          </div>`);

            new_items.push($new_item[0]);
          });

          if (response.pages < 2) {
            load_more.prop("disabled", true);
            load_more.text("No more posts");
          } else {
            load_more.prop("disabled", false);
            load_more.text("Load more");
          }

          $grid.isotope("remove", $grid.isotope("getItemElements"));

          $grid
            .isotope({
              filter: $filters
            })
            .isotope("insert", new_items)
            .imagesLoaded()
            .progress(function () {
              $grid.isotope("layout");
            });

          updateFilterCount(response.post_count);

          // Return false as we're at the end
          return false;

          // If the AJAX function returned no data
        } else {}
      },
      error: function (xhr, status, error) {
        console.log("There was an error", error);
      }
    });
  });

  /**
   * Reset all filtering
   */
  $(".filter-box__reset-filter").click(function () {

    has_run = false;

    $.ajax({
      type: "POST",
      dataType: "json",
      url: ajax_get_posts_object.ajax_url,
      data: {
        security: ajax_get_posts_object.ajax_nonce,
        action: "ajax_get_posts",
        current_page: 1
      },
      beforeSend: function (xhr) {
        console.log("Resetting");
      },
      success: function (response) {
        if (response) {
          console.log(response);

          // An array to store new items added via AJAX
          var new_items = [];

          // Run through JSON
          $.each(response.posts, function (key, value) {
            var $new_item = $(`<div class="grid-item article-post-card ${value.category[0]["slug"]} ${value.tags ? value.tags[0]["slug"] : ''}">
              <a class="article-post-card__anchor" href=" ${value.permalink}" alt="${value.title}">
                    <div class="article-post-card__featured-image-container">
                      <div class="article-post-card__overlay"></div>
                      <div class="article-post-card__featured-image">
                          ${value.thumbnail}
                      </div>

                      <div class="article-post-card__category-label">
                          ${value.category[0]["name"]}
                      </div>

                  </div>

                    <div class="article-post-card__content-wrapper">

                      <div class="article-post-card__publish-date">
                          <time class="updated" datetime="${value.post_time}">${value.date}</time>
                      </div>

                      <div class="article-post-card__title">
                          ${value.title}
                      </div>

                      <div class="article-post-card__excerpt">
                        ${value.introduction}
                      </div>

                  </div>
              </a>
          </div>`);

            new_items.push($new_item[0]);
          });

          $grid.isotope("remove", $grid.isotope("getItemElements"));

          $grid
            .isotope({
              filter: "*"
            })
            .isotope("insert", new_items)
            .imagesLoaded()
            .progress(function () {
              $grid.isotope("layout");
            });

          updateFilterCount(response.post_count);

          if (response.pages < 2) {
            load_more.prop("disabled", true);
            load_more.text("No more posts");
          } else {
            load_more.prop("disabled", false);
            load_more.text("Load more");
          }

          // Return false as we're at the end
          return false;

          // If the AJAX function returned no data
        } else {}
      },
      error: function (xhr, status, error) {
        console.log("There was an error", error);
      }
    });

    // Hide added divs
    $(".filters").hide();
    $(".filter-box__applied-filters-area").hide();

    // Uncheck all checkboxes
    $(".filter-box__toggler").removeClass("filter-box__toggler--disabled");
    $(".filter-box__toggler input").prop("checked", false);
    $(".filters input").prop("checked", false);

    // Reset text on buttons
    $('.toggler--category .toggle').text("Category");
    $('.toggler--tag .toggle').text("Tag");
  });

  /**
   * Load more posts function: on click of load more, makes an AJAX request to get more posts.
   * Takes into consideration categories and tags
   */
  $("button.load-more-posts").click(function (e) {
    e.preventDefault();

    load_more.prop("disabled", true);

    if (has_run == false) {
      next_page = 2;
    };

    // Perform AJAX request
    $.ajax({
      type: "POST",
      dataType: "json",
      url: ajax_get_posts_object.ajax_url,
      data: {
        action: "ajax_get_posts",
        security: ajax_get_posts_object.ajax_nonce,
        current_page: next_page,
        categories: $selectedCategories,
        tags: $selectedTags,
      },
      beforeSend: function (xhr) {
        load_more.text("Fetching...");
      },
      success: function (response) {
        if (response) {
          console.log(response);

          // An array to store new items added via AJAX
          var new_items = [];

          // Run through JSON
          $.each(response.posts, function (key, value) {
            var $new_item = $(`<div class="grid-item article-post-card ${value.category[0]["slug"]} ${value.tags ? value.tags[0]["slug"] : ''}">
              <a class="article-post-card__anchor" href=" ${value.permalink}" alt="${value.title}">
                    <div class="article-post-card__featured-image-container">
                      <div class="article-post-card__overlay"></div>

                      <div class="article-post-card__featured-image">
                          ${value.thumbnail}
                      </div>

                      <div class="article-post-card__category-label">
                          ${value.category[0]["name"]}
                      </div>

                  </div>

                    <div class="article-post-card__content-wrapper">

                      <div class="article-post-card__publish-date">
                          <time class="updated" datetime="${value.post_time}">${value.date}</time>
                      </div>

                      <div class="article-post-card__title">
                          ${value.title}
                      </div>

                      <div class="article-post-card__excerpt">
                        ${value.introduction}
                      </div>

                  </div>
              </a>
          </div>`);

            new_items.push($new_item[0]);
          });

          // Add the new items to the grid
          $grid
            .isotope("insert", new_items)
            .imagesLoaded()
            .progress(function () {
              $grid.isotope("layout");
            });

          updateFilterCount(response.post_count);

          if (next_page >= response.pages) {
            load_more.text("No more posts to load");
          } else {
            // Undo Button Disable
            load_more.text("Load more");
            load_more.prop("disabled", false);
          }

          // Update boolean so that page count is not reset
          has_run = true;

          next_page++;

          // Return false as we're at the end
          return false;

          // If the AJAX function returned no data
        } else {
          load_more.remove();
        }
      },
      error: function (xhr, status, error) {
        console.log("There was an error", error);
      }
    });
  });

  /**
   * A change event for the category toggler so we can tell the script to add the div containing categories
   */
  $(".toggler--category input").change(function () {
    if ($(this).is(":checked")) {
      $(".toggler--tag").addClass("filter-box__toggler--disabled");
      $(".filter-box__categories").show();
    } else {
      $(".toggler--tag").removeClass("filter-box__toggler--disabled");
      $(".filter-box__categories").hide();
    }
  });

  /**
   * A change event for the tag toggler so we can tell the script to add the div containing tags
   */
  $(".toggler--tag input").change(function () {
    if ($(this).is(":checked")) {
      $(".toggler--category").addClass("filter-box__toggler--disabled");
      $(".filter-box__tags").show();
    } else {
      $(".toggler--category").removeClass("filter-box__toggler--disabled");
      $(".filter-box__tags").hide();
    }
  });

  /**
   * Update the count of returned items from Isotope
   */
  function updateFilterCount(post_count) {
    $resultCount.text("Showing " + $items.filteredItems.length + " of " + post_count + " results");
  }

});

Is there a way I can avoid repetition, however I do have to perform an AJAX request each time to correctly get data based on filtering.

\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.