Let's create a product page with filters together

Let's create a product page with filters together

using HTML, CSS and Javascript for beginners

This tutorial will walk you through how to make a product page that filters through the different products.

You can reuse this to make any page that needs to filter through products such as an e-commerce website or a restaurant menu.

Key concepts covered

  • arrays
  • objects
  • forEach()
  • DOMContentLoaded
  • map, reduce, and filter
  • innerHTML
  • includes method

First Thing First, we set up the project

  1. create a folder
  2. create an HTML file (index.html)
  3. create a CSS file (styles.css)
  4. create a Javascript file (app.js)
  5. create an image folder with our products' images

Starting with the skeleton (HTML)

Set up:

in the HTML file (for me it's index.html), include the CSS and javascript files.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Products</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <section class="products"> </section>
    <script src="app.js"></script>
</body>
</html>

Now we can start:

Title:

<div class="title">
        <h1>Our Products</h1>
        <hr />
</div>

Main Container:

 <section class="products">
      <!-- title -- >
      <div class="title">
        <h1>Our Products</h1>
        <hr />
      </div>
      <!-- Filter buttons -- >
      <!-- products -- >
      <div class="section-center">
      <!-- start product -- >
      <!-- end product -- >
      </div>
    </section>

Product:

<!-- start product-->
        <article class="product">
          <img src="./images/Leather.jpg" alt="Leather Heels" class="photo" />
          <div class="product-info">
            <header>
              <h4>Brown Leather Heels</h4>
              <h4 class="price">$66.55</h4>
            </header>
            <p class="product-text">
              Anim culpa cupidatat consectetur qui proident sint sunt dolor esse
              tempor eu ullamco nulla. Excepteur exercitation laborum eu
              laborum.
            </p>
          </div>
        </article>
<!-- end product-->

Filter Buttons:

   <!-- Filter buttons-->
      <div class="btn-container">
        <div class="filter-btn" type="button">All</div>
        <div class="filter-btn" type="button">Sneakers</div>
        <div class="filter-btn" type="button">Heels</div>
        <div class="filter-btn" type="button">Boots</div>
      </div>

Let's make it pretty (CSS)

Set up:

  1. choose the colour palette: I prefer using Coolors

  2. choose fonts: I prefer using Google Fonts

@import url('https://fonts.googleapis.com/css2?family=Oswald&display=swap');

:root {
    --pacific-blue: #00b2caff;
    --yale-blue: #1d4e89ff;
    --cameo-pink: #efbbcc;
}
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body {
    width: 100%;
    height: 100vh;
    font-family: 'Oswald', sans-serif;
}
section.products{
    padding: 2.5rem 0;
}

Title :

.title{
margin: auto;
width: 115px;
color: var(--yale-blue);
padding-bottom: 1.5rem;
}
hr{
    border-radius: 5px;
    background-color: var(--pacific-blue);
    border: 1px solid var(--pacific-blue);
}

Main Container:

.section-center{
    width: 90vw;
    margin: 0 auto;
    max-width: 1170px;
    display: grid;
    gap: 3rem 2rem;
    justify-items: center;
}

Product:

.photo{
    object-fit: cover;
    height: 160px;
    border: 0.25rem solid var(--pacific-blue);
    border-radius: 0.5rem;
    max-width: 110px;
}
.product {
    display: flex;
    justify-content: space-evenly;
    color: var(--yale-blue);
    max-width: 25rem;
    gap: 1rem 2rem;
  }
  .product-info{
    display: flex;
flex-direction: column;
justify-content: space-evenly;
  }
  .product header{
    display: flex;
justify-content: space-between;
}
  .price {
    color: var(--pacific-blue);
  }
  .product-text {
    margin-bottom: 0;
    text-align: justify;
  }

Filter Buttons:

  .btn-container {
    margin-bottom: 2rem;
    display: flex;
    justify-content: center;
  }
  .filter-btn {
    background: transparent;
    border: 0.25rem solid var(--cameo-pink);
font-weight: bold; 
   margin: 0 0.5rem;
    border-radius: 0.5rem;
    padding: 0.5rem;
    color:var(--yale-blue);
    cursor: pointer;
    transition: all 0.3s linear;
  }
  .filter-btn:hover {
    background:  var(--yale-blue);
    color: var(--cameo-pink);
  }

Media Queries:

  @media screen and (min-width: 1200px) {

    .section-center {
        width: 95vw;
        grid-template-columns: 1fr 1fr;
      }
      .product {
        grid-template-columns: 225px 1fr;
        gap: 0 1.25rem;
        max-width: 40rem;
      }
    }

Make it dynamic (Javascript)

Include products:

const products = [
  {
    id: 1,
    title: "Nike airforce 1",
    category: "Sneakers",
    price: 66.99,
    image: "./images/airforce.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 2,
    title: "Brown Leather Heels",
    category: "Heels",
    price: 66.55,
    image: "./images/Leather.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 3,
    title: "Brown Male Boots",
    category: "Boots",
    price: 55.99,
    image: "./images/Brown.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 4,
    title: "Red Converse All Star",
    category: "Sneakers",
    price: 55.99,
    image: "./images/Converse.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 5,
    title: "Weeding Heels",
    category: "Heels",
    price: 55.99,
    image: "./images/Weeding.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 6,
    title: "Male Boots",
    category: "Boots",
    price: 72.55,
    image: "./images/Male.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 7,
    title: "Floral Heels",
    category: "Heels",
    price: 42.99,
    image: "./images/Floral.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 8,
    title: "Jordan 1 Retro",
    category: "Sneakers",
    price: 76.99,
    image: "./images/Jordan.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
  {
    id: 9,
    title: "Midwest Trekking",
    category: "Boots",
    price: 72.99,
    image: "./images/Midwest.jpg",
    description: `Anim culpa cupidatat consectetur qui proident sint sunt dolor esse tempor eu ullamco nulla. Excepteur exercitation laborum eu laborum.`,
  },
];

Main container:

// get parent element
const section center = document.querySelector(".section-center");
const btnContainer = document.querySelector(".btn-container");
// load items
window.addEventListener("DOMContentLoaded", function () {
  displayProductsItems(products);
  displayProductsButtons();
});

Product:

function displayProductsItems(productsItems) {
  let displayProducts = productsItems.map(function (item) {
    return `<article class="product">
  <img src= ${item.image} alt=${item.title} class="photo" />
  <div class="product-info">
    <header>
      <h4>${item.title}</h4>
      <h4 class="price">$${item.price}</h4>
    </header>
    <hr />
    <p class="product-text">
    ${item.description}
    </p>
  </div>
</article>`;
  });

  displayProducts = displayProducts.join("");
  sectionCenter.innerHTML = displayProducts;
}

Filter Buttons:

function displayProductsButtons() {
  const categories = products.reduce(
    function (values, item) {
      if (!values.includes(item.category)) {
        values.push(item.category);
      }
      return values;
    },
    ["All"]
  );
  const categoryBtns = categories
    .map(function (category) {
      return `<div class="filter-btn" type="button" data-id=${category}>${category}</div>`;
    })
    .join("");
  btnContainer.innerHTML = categoryBtns;
  const filterBtns = btnContainer.querySelectorAll(".filter-btn");
  filterBtns.forEach(function (btn) {
    btn.addEventListener("click", function (e) {
      const category = e.currentTarget.dataset.id;
      const productsCategory = products.filter(function (productsItems) {
        if (productsItems.category === category) {
          return productsItems;
        }
      });
      if (category === "All") {
        displayProductsItems(products);
      } else {
        displayProductsItems(productsCategory);
      }
    });
  });
}

Conclusion:

To see the completed project: Project

To see the final code: Code

Thank you for your time. I hope you found it useful. ❤️

If you enjoyed this article and want to be the first to know when I post a new one, you can follow me on Twitter @habibawael02 or here @HabibaWael