Filtering Component using Vanilla JS and HTML Content Template Element.
Tags: javascript, webdev, tutorial, wecoded
Published: 2020-11-24 17:46:14

_Create your own filtering component using TailwindCSS, HTML content `<template>` element, and JavaScript (yeap, no JS framework needed). Not all projects require the use of frameworks and this is a solution for those simpler projects._

> FYI You can skip the explanation and take a look at the whole code in the CodePen at the end of this article. 

Creating a filtering component can be done by writing hundreds of lines of HTML code and controlling the material shown by changing the 'display' property from 'none' to 'block'. 

That's fine, but you can also use the power of JavaScript so as to not repeat the same HTML code over and over again. All you need to do to get started is to create one single template in your HTML and learn how to copy said template and manipulate it using JavaScript.   

## TailWindCSS

[TailWind CSS](https://tailwindcss.com/) is a utility-first CSS framework that helps you design directly on your markup. Installation can be a little tricky, but you can always just add the CDN link `<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">` at the `<head>` of your HTML document to get you started quickly. 

For those of you that already know Tailwind, have you checked out version 2.0? They have added some really cool features like dark mode and animations. I'll be having a deeper look at it myself this weekend. #funcoding

## HTML

In your HTML you want to look into creating your own [`<template>` tag](https://www.w3schools.com/tags/tag_template.asp). The content held within the template tag will be hidden when the page loads. You have to use JavaScript in order to create a clone of this template and attach it to the body of your document for it to be displayed.  

For this mini-project example, we will have a few buttons at the top of the page and below them, we will display a few cards with information on them. No need to know what the information is right now, just the structure of it.  

You can give this project whatever style you want, but what's important is to add a `btnFilter` class to all the filtering buttons. The buttons will also need their own identifying IDs. The section(or div) element that will contain all the copies/clones of the template will need an ID (`resourcesContainer`) as well as the elements we want to manipulate with JavaScript that are found within the template (`resource`, `title`, `description`, and `tagsContainer`). 

```html 
<!-- buttons section -->
<section class="mt-10 text-center">
      <button id="all" class="btnFilter text-gray-100 font-bold py-1 px-3 my-4 rounded-full border-2 shadow-2xl bg-gradient-to-b from-blue-700 to-teal-400 md:my-0 md:mx-2 hover:from-blue-600 hover:to-teal-300 hover:border-teal-400 focus:outline-none focus:shadow-outline">
        All
      </button>
      <button id="html" class="btnFilter text-gray-100 font-bold py-1 px-3 my-4 rounded-full border-2 shadow-2xl bg-gradient-to-b from-blue-700 to-teal-400 md:my-0 md:mx-2 hover:from-blue-600 hover:to-teal-300 hover:border-teal-400 focus:outline-none focus:shadow-outline">
        HTML
      </button>
      <button id="css" class="btnFilter text-gray-100 font-bold py-1 px-3 my-4 rounded-full border-2 shadow-2xl bg-gradient-to-b from-blue-700 to-teal-400 md:my-0 md:mx-2 hover:from-blue-600 hover:to-teal-300 hover:border-teal-400 focus:outline-none focus:shadow-outline">
        CSS
      </button>
      <button id="js" class="btnFilter text-gray-100 font-bold py-1 px-3 my-4 rounded-full border-2 shadow-2xl bg-gradient-to-b from-blue-700 to-teal-400 md:my-0 md:mx-2 hover:from-blue-600 hover:to-teal-300 hover:border-teal-400 focus:outline-none focus:shadow-outline">
        JavaScript
      </button>
    </section>

<!-- RESOURCES CONTAINER section div -->
<section id="resourcesContainer" class="w-full flex flex-wrap content-center justify-around mx-auto px-2 py-5 md:px-4 lg:px-10">

  <!-- TEMPLATE resource card -->
  <template>
    <!-- CARD -->
    <article id="resource" class="flex flex-col overflow-hidden rounded-lg shadow-2xl w-64 m-4 bg-teal-400 text-center lg:w-3/12 lg:m-8">
        <div class="rounded-md bg-blue-100 text-blue-600 m-4 p-2 flex-grow flex flex-col justify-between">
          <header class="font-bold text-lg text-blue-700 underline">
            <h2>
              <a id="title" href="#" target="_blank">
              </a>
            </h2>
          </header>
          <p id="description">
          </p>
          <!-- TAGS -->
          <div id='tagsContainer' class="pt-4 flex justify-around flex-wrap"> 
          </div>
          <!-- TAGS end -->
        </div>
      </article>
      <!-- CARD end -->
  </template>
  <!-- TEMPLATE resource card end -->

</section>
<!-- RESOURCES CONTAINER section div end -->
```

I won't explain the classes as, aside from `btnFilter`, all are TailWind CSS classes. These help style my component without adding a single line of CSS code, but for the sake of explaining a few: `mt-10` just means `margin-top: 2.5rem;`, `md:my-0` means at medium-sized screens `margin-top` and `margin-bottom` are set to `0px`, and `hover:border-teal-400` means that on hover the element's border will be a shade of the color teal. Most can be quite self-explanatory. 

With TailWind, I recommend starting the design for mobile screens and add extra classes for bigger screens after that.    
   
## JavaScript

Now comes the fun part! Let's start this part by collecting a few initial resources (or any dummy data) and adding them to our `dataResources` array. 

```javascript
const dataResources = [
  {'title': 'FlexBox Froggy',
   'link': 'https://flexboxfroggy.com/',
   'description': 'A game where you help Froggy and friends by writing CSS code!',
   'tags': ['all', 'css', 'game']
  },
  {'title': 'freeCodeCamp',
   'link': 'https://www.freecodecamp.org/',
   'description': 'Learn to code at home. Build projects. Earn certifications.',
   'tags': ['all', 'html', 'css', 'js', 'certification']
  },
  {'title': 'State of JavaScript',
   'link': 'https://stateofjs.com/',
   'description': 'Find out which libraries developers want to learn next, satisfaction ratings, and much more.',
   'tags': ['all', 'js', 'survey']
  }
]
```

Now for our first function, we are going to filter the dataResources array to match the category with the tags in each resource. If any of the tags match the category chosen then we will clone the template of the card and use the data of said resource on it. Then we will take this newly cloned card and append/attach it to the resources div/section container. The same will be repeated for all the resources that match to create a new card for each. For those that don't match the category, nothing will happen.  

```javascript 
const resources = document.querySelector("#resourcesContainer");

let category="";
let filteredDataResources = [];

function filterResources(category) {
  // filter data
  filteredDataResources = dataResources.filter( resourceData => {
    if (resourceData.tags.includes(category)) {
       // clone new card
       const resourceCard = copyTemplateCard(resourceData);
       // attach new card to container
       resources.appendChild(resourceCard);
    } 
  })
}

// call filterResources function to display all cards with an `all` tag on their tag list.

filterResources("all");
```

How are the new cards getting cloned from the template? For that let's dive into our second function, which got called each time a resource would pass through the filter from our filterResources function. 

```javascript
const template = document.querySelector("template"); 
function copyTemplateCard(resourceData) {
  // clone template
  const resourceTemplate = document.importNode(template.content, true);
  const card = resourceTemplate.querySelector("#resource");
  // insert title information from array
  const title = card.querySelector("#title");
  title.innerText = resourceData.title;
  title.href = resourceData.link;
  // insert description information from array
  const description = card.querySelector("#description");
  description.innerText = resourceData.description;
  // insert tag information from array  
  tagsContainer = card.querySelector("#tagsContainer");
  // map though tags to create a tag element for each tag in the array
  resourceData.tags.map(resourceDataTag => {
    const individualTag = document.createElement("span")
    // create a tag for all tags except the first one called 'all'
    if ( resourceDataTag !== 'all' ) {
      // add styles
      individualTag.classList = "inline-block bg-teal-400 text-blue-100 rounded-full px-3 py-1 mb-1 text-sm font-semibold"
    // add text
    individualTag.innerHTML = `#${resourceDataTag}`
    // append/atach tag to tags container
    tagsContainer.appendChild(individualTag)
    } 
  })
  // return the new cloned card with the information from the array inserted into it.
  return card;
}
```

Almost done with our functions. Now we want to be able to change the category of the filtered items shown. We can do that with a short function. Within it, we will clear the container holding all the cards. You want to do this because otherwise every time you select a new category new cards will be cloned without getting rid of the previous cards. 

After that, we will change our `category` variable to equal the ID of the button pressed (which is why it was important for each button to have its own unique ID). With the category changed we can call the filterResourses function with the new category as a parameter, so as to filter the dataResources array once more and create our new cards according to this new category. 

```javascript
function fillResourcesContainer(e) {
  resources.innerHTML = "";
  category = this.id;
  resources;
  filterResources(category)
}
```

Now just add an event listener to each button and there you go! 

```javascript 
const btnFilter = document.querySelectorAll(".btnFilter")

btnFilter.forEach(function(btn) {
    btn.addEventListener("click", fillResourcesContainer);
});
``` 

## Demo

Feel free to fork and play with this CodePen and add as many different features to it as you see fit. An example that I did elsewhere was to change the color of the tags according to their category using `switch statement`.

{% codepen https://codepen.io/santiagocodes/pen/NWrVapp %}

---

If you liked what you read hit that :heart: on the left or wherever it is. If you really liked it don’t forget to share it with the community by hitting that dot-dot-dot icon near the heart. 

:computer: **article.close()**