DEV Community

Building a Lightweight Product Filter with Vanilla JavaScript

While building a small e-commerce project, I wanted users to filter products instantly without refreshing the page. Instead of relying on a frontend framework, I opted for a simple solution using HTML data attributes, vanilla JavaScript, and a little CSS. The goal was straightforward: let visitors filter items by size while keeping the interface fast, responsive, and easy to maintain.

HTML Structure

Each product card stores its information in data-* attributes. This keeps the markup clean and makes filtering straightforward.

<div class="filters">
  <button class="filter-btn" data-filter="all">All</button>
  <button class="filter-btn" data-filter="small">S</button>
  <button class="filter-btn" data-filter="medium">M</button>
  <button class="filter-btn" data-filter="large">L</button>
</div>

<div class="product-grid">
  <div class="product-card" data-size="medium" data-style="cargo">Cargo Shorts</div>
  <div class="product-card" data-size="large" data-style="chino">Chino Shorts</div>
  <!-- More product cards -->
</div>

Using data attributes means you can add new filter categories later without changing your overall structure.

JavaScript Filtering Logic

The filtering logic listens for button clicks and simply shows or hides product cards based on the selected size.

const filterButtons = document.querySelectorAll(".filter-btn");
const productCards = document.querySelectorAll(".product-card");

filterButtons.forEach((button) => {
  button.addEventListener("click", () => {
    const filterValue = button.dataset.filter;

    productCards.forEach((card) => {
      const cardSize = card.dataset.size;
      if (filterValue === "all" || cardSize === filterValue) {
        card.classList.remove("hidden");
      } else {
        card.classList.add("hidden");
      }
    });

    filterButtons.forEach((btn) => btn.classList.remove("active"));
    button.classList.add("active");
  });
});

The implementation is intentionally minimal:

  • No external libraries
  • No virtual DOM
  • No unnecessary complexity
  • Easy to extend with additional filters like color, category, or brand

Adding Smooth Transitions

Instead of having products instantly disappear, a small transition creates a smoother user experience.

.product-card {
  overflow: hidden;
  transition: opacity 0.3s ease, max-height 0.3s ease;
}

.product-card.hidden {
  opacity: 0;
  max-height: 0;
  margin: 0;
  padding: 0;
}

These subtle animations make the interface feel much more polished without affecting performance.

Why This Approach Works

For many projects, especially smaller storefronts or internal dashboards, a lightweight solution is often the better choice. Some advantages include:

  • Fast page interactions
  • Zero dependencies
  • Easy debugging
  • Beginner-friendly code
  • Excellent browser support

Because everything is driven by HTML data attributes, you can easily expand this approach to support:

  • Multiple filter groups
  • Search functionality
  • Sorting
  • Category filtering
  • Price ranges
  • Combined filters

Final Thoughts

Vanilla JavaScript is still an excellent choice for interactive UI components like product filtering. Unless your application truly requires a large framework, simple DOM manipulation can provide an experience that's fast, maintainable, and easy to understand. Sometimes the simplest solution ends up being the most reliable. This filtering pattern is reusable across almost any catalog or gallery, making it a handy technique to keep in your frontend toolbox.

Comments

No comments yet. Start the discussion.