Visualize javascript's array.filter() by building product filter buttons

Visualize javascript's array.filter() by building product filter buttons

JavaScript's array.filter method is an array method you've likely heard of, read about or even used while following a few tutorials or solving javascript algorithm questions. It's a very important array method you'll use a lot.

The array.filter method...

  • Takes an array

  • Takes a filter condition

  • Loops over the array

  • Checks for array elements that satisfy the filter condition and passes those elements into a new array

  • Leaves the original array intact

If this is your first time reading about the filter method then check MDN's detailed explanation or this equally explanatory article on FreeCodeCamp.

If this is your first time checking out the Visualizing JavaScript Series then check out Article Zero of this series.

What we'll be building

You'll visualize the array.filter method by making product category filter buttons for product list of some yummy cakes.

The product section will have buttons that filter items shown on the screen based on their categories. This is a common feature on ecommerce websites and you'll learn how to make a simplified version of it using JavaScript

You've read a brief explanation of the array.filter() method and you've seen what you'll be making, now, let's get started.

Creating the filter Buttons

I'll start the project by copying the HTML from the array.map article but I'll make a few modifications to the code.

<!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>Document</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="btn-container">
      <button class="filter-all" type="button" data-id="all">All</button>
      <button class="filter-btn" type="button" data-id="cakes">Cakes</button>
      <button class="filter-btn" type="button" data-id="cup-cakes">
        Cupcakes
      </button>
      <button class="filter-btn" type="button" data-id="wedding">
        Wedding
      </button>
    </div>
    <div class="section-center">
      <article>
        <img src="/assets/Doughnuts.jpg" alt="" />
        <div class="item-info">
          <div class="item-div">
            <h2 class="item">buttermilk pancakes</h2>
            <h4 class="item-price">$15</h4>
          </div>
          <p class="item-desc">
            Lorem ipsum dolor sit amet consectetur, adipisicing elit.
            Reprehenderit at tenetur ratione eaque.
          </p>
        </div>
      </article>
    </div>

    <script src="app.js"></script>
  </body>
</html>

The changes made to the original code are:

  • Created a div with class of btn-container for the filter buttons.

  • Added four buttons to the container and gave them classes of filter-btn and filter-all.

  • Added a data-id to each button to serve as a unique identifier, data-id allows HTML elements to be used in unique ways. You'll see a bit more of this later in the article.

Styling the filter Buttons

You can just copy and paste the CSS to save some time

@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,500;1,200&display=swap');

*{
    margin: 0;
    padding: 0;
    text-decoration: none;
    font-family: Poppins;
}
body{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items:center ;
    padding-top: 2em;
}
button{
    cursor: pointer;
    background-color: red;
    color: white;
    height: 50px;
    width: 100px;
    border: none;
    padding: 2px;
    font-weight: bold;
    font-size: 1em;
    border-radius: 5%;
}
button:hover{
    background-color: white;
    border: 2px solid red;
    color: red;
    transition: .7s ease;
}
button:focus{
    background-color: whitesmoke;
    border-bottom: 4px solid red;
    color: red;
}
.section-center{
    padding: 2em;
    display: flex;
    justify-content: center;
    gap: 20px;
    flex-wrap: wrap;
}
h4{
    font-size: 1.2em;
}
article{
    display: flex;
    flex-direction: column;
    width: 16em;
    box-shadow: 5px 5px 10px 0.1px grey;
    margin-bottom: 10px;
    border-radius: 3%;
}
img{
    max-width: 100%;
    border-top-left-radius: 3%;
    border-top-right-radius: 3%;
}
.item-info{
    padding: .5rem;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

Making the filter buttons Functional

We'll use the javascript code from the array.map article since it contains the array of products and the displayMenuItems function we need to map over the array and display the products on the screen.

I've edited the product description in the menu array and changed it from the lorem placeholder text to something more readable.

const menu = [
    {
      id: 1,
      title: "Triple layer Cake",
      price: "$15.99",
      img: "https://unsplash.com/photos/kPxsqUGneXQ",
      desc: "Exquisite, moist layers of flavorful cake with delectable fillings. A feast for the senses.",
    },
    {
      id: 2,
      title: "Vanilla goodness",
      price: "$13.99",
      img: "https://unsplash.com/photos/vdx5hPQhXFk",
      desc: "A heavenly delight of creamy vanilla perfection. Indulge in pure bliss with every bite.",
    },
    {
      id: 3,
      title: "Chocolate cupcake",
      price: "$6.99",
      img: "https://unsplash.com/photos/6SHd7Q-l1UQ",
      desc: "A miniature delight of rich, moist chocolate perfection. Indulge in a moment of pure bliss.",
    }]

   const section = document.querySelector('.section-center')

    function displayMenuItems(){
        let displayMenu = menu.map(function(item){ 
            return `
            <article>                           
           <img src=${item.img} alt="">
           <div class="item-info">
               <div class="item-div">
                  <h2 class="item">${item.title}</h2>
                  <h4 class="item-price">${item.price}</h4>
               </div>
               <p class="item-desc">${item.desc}</p>
               <button class>${item.btn}</button>
               </div>
         </article>
            `
        })
        section.innerHTML = displayMenu
    }

    displayMenuItems()

To make the buttons functional, you need to select the filter buttons using the querySelectorAll, using the querySelectorAll method on classes creates a NodeList for those classes (you can think of a NodeList as a mini array grouping the classes) this NodeList is then assigned to a variable named filterButtons

const filterButtons = document.querySelectorAll(".filter-btn")

The button classes are in a NodeList and this list can be looped over using the forEach array method shown below. The forEach method in the code snippet below goes over the filterButtons list containing the filter buttons, takes each button on the list and runs a function on that button.

filterButtons.forEach(btn => {
  // rest of the code here
})
filterButtons.forEach(btn => {
  btn.addEventListener("click", (e) => {
    //the rest of the code
}

The code snippet above gives each button an event listener that takes an event type of click, giving something an event listener of click means when it is 'clicked' a function should run.

(e) => {
    const category = e.currentTarget.dataset.id
    const menuCategory = menu.filter(item => {if(item.category === category){
      return item
    }})    
    displayMenuItems(menuCategory);
  })

The code snippet above does several things but we'll break it into chunks.

  1.  const category = e.currentTarget.dataset.id
    

    The function takes an event e as an argument and e.currentTarget.dataset.id checks the for data-id of the current button being clicked and assigns the data-id to a variable named category

  2.  const menuCategory = menu.filter(item => {
       if(item.category === category) {
           return item
         }})
    
    • The menu.filter method goes over the menu array and checks the category of each item of the menu array using the if statement.

    • The if statement checks if the category of an item in the menu array matches the category of the button clicked (remember we added a click listener to each button), when the correct item is found, the menu.filter returns the item into a new array called menuCategory.

You can remove the if statement from the code above and rewrite it as

    const menuCategory = menu.filter(item => {
          return item.category === category
        })

The code still does the same thing but without the need for the if statement

  1.  displayMenuItems(menuCategory)
    

    Finally, we set the array to be mapped over by the displayMenuItems function to the menuCategory array instead of the menu array.

Making the all button Functional

You might remember there's a button with a data-id of all, the all button is meant to display all the products in the array, the easiest way to do this is to pass the menu array back into the displayMenuItems function.

You can do this in just two steps.

  1. Select the all button using a querySelector

     const filterAll = document.querySelector(".filter-all")
    
  2. Add a click event listener that runs a function, the function passes the menu array(the original array) back into the displayMenuItems function that displays all the products.

     filterAll.addEventListener("click", () => {
       displayMenuItems(menu)
     })
    

    This resets the page to its original state with all the products displayed.

    You can see the complete code below.

     const section = document.querySelector(".section-center");
     const filterButtons = document.querySelectorAll(".filter-btn")
     const filterAll = document.querySelector(".filter-all")
    
     const menu = [
       {
         id: 1,
         title: "Triple layer Cake",
         category: "cakes",
         price: "$15.99",
         img: "https://unsplash.com/photos/kPxsqUGneXQ",
         desc: "Exquisite, moist layers of flavorful cake with delectable fillings. A feast for the senses."
       },
       {
         id: 2,
         title: "Vanilla goodness",
         category: "wedding",
         price: "$13.99",
         img: "https://unsplash.com/photos/vdx5hPQhXFk",
         desc: "A heavenly delight of creamy vanilla perfection. Indulge in pure bliss with every bite."
       },
       {
         id: 3,
         title: "Chocolate cupcake",
         category: "cup-cakes",
         price: "$6.99",
         img: "https://unsplash.com/photos/6SHd7Q-l1UQ",
         desc: "A miniature delight of rich, moist chocolate perfection. Indulge in a moment of pure bliss."
       },
     ];
    
     filterButtons.forEach(btn => {
       btn.addEventListener("click", (e) => {
         const category = e.currentTarget.dataset.id
         const menuCategory = menu.filter(item => {
           return item.category === category
         })    
         displayMenuItems(menuCategory);
       })
     })
    
     filterAll.addEventListener("click", () => {
       displayMenuItems(menu)
     })
    
     function displayMenuItems(menuItems) {
       let displayMenu = menuItems.map(function (item) {
         return `
           <article data-id="${item.id}">
             <img src=${item.img} alt="">
             <div class="item-info">
               <div class="item-div">
                 <h2 class="item">${item.title}</h2>
                 <h4 class="item-price">${item.price}</h4>
               </div>
               <p class="item-desc">${item.desc}</p>
             </div>
           </article>
         `;
       });
    
       section.innerHTML = displayMenu.join('');
     }
    
     displayMenuItems(menu);
    

Conclusion

You have visualized the array.filter method by building functional product category filter buttons for a product list, you've used some javascript syntax like the forEach array method, addEventListener, and the if statement to build the project.

You have also briefly heard of a NodeList, you can read more about NodeList on MDN

Please practice a bit more and try rebuilding the entire project from scratch so you can get a better grip on it. If you have any questions, comments or suggestions, feel free to drop them in the comments or reach out to me on Twitter where I'm more active. See you in the next article