How to Code a Filterable Image Gallery with JavaScript

Animation. Screenshots illustrate how a gallery of Case Study images changes when a filter button is clicked.
Share on facebook
Share on twitter
Share on pinterest
Share on linkedin

Create your own filterable image gallery no plugins needed with plain vanilla JavaScript. The solution is quick and light-weight. Further, you can easily adapt the code snippet to fit your needs.

JavaScript (JS) is a great web development tool that manipulates a web page’s behavior. Improve user experiences and web browsing with interactivity to help users find information faster and complete tasks.

An image gallery can benefit from JavaScript by incorporating interactive “filters.” A user may click a button labeled with a tag to make the web browser only display images with that tag. I had used JavaScript to create a filterable Case Study gallery. Users click the button “Logos” to show Case Study thumbnails of logos I’ve made, or the button “Web Design / Development” to display only Case Studies in which I designed or coded web pages, web apps, or HTML emails.

What You’ll Learn:

This tutorial explains how to create an image gallery in HTML and CSS embellished with JavaScript to add interactive filters. The result is a light-weight filterable image gallery, no plugins required!

The JavaScript Source Code & My Adaptations:

I adapted this code from W3School’s tutorial of a similar nature. However, there are two big changes to resolve usability issues.

The first removes inline JavaScript from the HTML document. Inline JS poses a security threat and makes a web page more vulnerable to hijacking and cross-scripting attacks. I replaced the “onclick” HTML attributes with click event listeners in an external JavaScript document.

The second reverses the image display on page load. In the original tutorial, the images are all set to display: none, hiding them from view. The JavaScript shows the images right away using a function, but if users disable JavaScript, they will stare at a blank page. I instead manually applied the same CSS class that the function did right away as a no-JavaScript fallback.

Thirdly, I also made another big aesthetic change—I added animations to reveal the Case Study title on hover while simultaneously “zooming in” on the thumbnail image in the background. I cover this effect in my web development tutorial, “How to Code a CSS-Only Image Gallery with Animated Effects.”

Step-by-Step JavaScript Tutorial to Make a Filterable Image Gallery

1. Create an HTML document if needed.

I suggest linking to an external CSS and JavaScript documents for boosted performance and security, and I handled this through WordPress’s functions.php file. However, for testing purposes and this tutorial, I’ll keep everything in one document. Add <style> tags to the HTML header, and <script> tags before the closing </body> tag. Once your document is working as expected, move these sections to your desired external locations.

2. Create the HTML for the filter buttons.

The HTML code has two primary parts: the array of filter buttons and the array of thumbnail images.

The filter buttons are easy to code. Give each button a unique ID that clearly identifies it and shows its relationship to other HTML elements. I adhere to the CSS Guidelines established by Harry Roberts to name my elements. Then give the buttons a CSS class that groups them as buttons, and assign the “Show All” button a CSS class to designate it the active button. Finally, beneath the buttons, enter a paragraph that displays some sort of error message for web browsers that disabled JavaScript.

<div id="gallery__filters">
  <button id="filter--show-all" class="gallery__filter tag--active"> Show all</button>
  <button id="filter--logos" class="gallery__filter"> Logos</button>
  <button id="filter--identity" class="gallery__filter"> Identity / Branding</button>
  <button id="filter--illustration" class="gallery__filter"> Illustration</button>
  <button id="filter--web-development" class="gallery__filter"> Web Design / Development</button>
  <button id="filter--packaging" class="gallery__filter"> Packaging</button>
  <p id="gallery__filters__message">Sorry, these filters won&rsquo;t work without JavaScript enabled. Please enable JavaScript, or simply click through the Case Studies below.</p>
</div>
<!-- #gallery__filters -->

3. Create the HTML for the thumbnail images.

The second HTML part, the thumbnail image gallery, is a bit more complicated because I chose to add hover effects.

Create a parent container for the whole gallery outside of and beneath the filter buttons from the previous step. The images will sit against each other using floats, so also add the clearfix CSS class to the parent container.

<div id="gallery__thumbs" class="gallery__row clearfix">



</div>
<!-- #gallery__thumbs -->

Inside the gallery parent container, each image needs:

  • tags for the filters;
  • CSS classes that collectively group them into columns plus thumbnails;
  • wrappers for the hover effects; and
  • spans for the titles that appear when the hover effects execute.

The thumb__wrapper turns each thumbnail into a background image. This way, you can avoid resizing and cropping each image to match each other. Instead, the thumbnails will fill the wrapper so they all have consistent sizes.

The first image looks like:

<div id="gallery__thumbs" class="gallery__row clearfix">

  <div class="gallery__column gallery__thumb filter--show tag--illustration tag--web-development">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/landscaping-app/">
        <div id="thumb--townhouse"><span class="thumb__caption">Townhouse Landscaping Proposal Web&nbsp;App</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

</div>
<!-- #gallery__thumbs -->

Next, continue to add images by copy-pasting the whole div.gallery__column.gallery__thumb and overwriting the contents with the new image info.

Finally, my full HTML looks like this:

<div id="gallery__filters">
  <button id="filter--show-all" class="gallery__filter tag--active"> Show all</button>
  <button id="filter--logos" class="gallery__filter"> Logos</button>
  <button id="filter--identity" class="gallery__filter"> Identity / Branding</button>
  <button id="filter--illustration" class="gallery__filter"> Illustration</button>
  <button id="filter--web-development" class="gallery__filter"> Web Design / Development</button>
  <button id="filter--packaging" class="gallery__filter"> Packaging</button>
  <p id="gallery__filters__message">Sorry, these filters won&rsquo;t work without JavaScript enabled. Please enable JavaScript, or simply click through the Case Studies below.</p>
</div>
<!-- #gallery__filters -->

<div id="gallery__thumbs" class="gallery__row clearfix">

  <div class="gallery__column gallery__thumb filter--show tag--illustration tag--web-development">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/landscaping-app/">
        <div id="thumb--townhouse"><span class="thumb__caption">Townhouse Landscaping Proposal Web&nbsp;App</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

  <div class="gallery__column gallery__thumb filter--show tag--logos tag--web-development tag--identity">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/smile-team/">
        <div id="thumb--smile"><span class="thumb__caption">Smile Team Logo, Identity, &amp;&nbsp;Emails</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

  <div class="gallery__column gallery__thumb filter--show tag--packaging">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/adelante/">
        <div id="thumb--adelante"><span class="thumb__caption">Adelante Shoe Co. Packaging</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

  <div class="gallery__column gallery__thumb filter--show tag--logos tag--illustration tag--web-development tag--identity">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/spc-turkey-trot/">
        <div id="thumb--turkey"><span class="thumb__caption">SPC Turkey Trot Logo &amp; Event Marketing Collateral</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

  <div class="gallery__column gallery__thumb filter--show tag--logos tag--identity">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/open-heart-parrot-rescue/">
        <div id="thumb--open"><span class="thumb__caption">Open Heart Parrot Rescue Logo &amp; Identity&nbsp;Concepts</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

  <div class="gallery__column gallery__thumb filter--show tag--logos tag--identity">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/spc-lawn-enforcement/">
        <div id="thumb--lawn"><span class="thumb__caption">SPC Lawn Enforcement Logo &amp;&nbsp;Identity</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

  <div class="gallery__column gallery__thumb filter--show tag--logos tag--identity">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/spc-safety/">
        <div id="thumb--safety"><span class="thumb__caption">SPC Safety &amp; Security Team Logo, Newsletter, &amp;&nbsp;Identity</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

  <div class="gallery__column gallery__thumb filter--show tag--identity">
    <div class="thumb__wrapper">
      <a href="https://www.laralee.design/case-studies/afcea-nova-2016-golf-tournament/">
        <div id="thumb--golf"><span class="thumb__caption">AFCEA NOVA 2016 Golf Tournament Brand&nbsp;Refresh</span></div>
      </a>
    </div>
    <!-- .thumb__wrapper -->
  </div>
  <!-- .gallery__column gallery__thumb -->

</div>
<!-- #gallery__thumbs -->

4. Style the filter buttons with CSS.

I remove the default button styles to create flat, red and white buttons. I also add an animation transition to animate the color-changing border. Also style the active filter button class. CSS allows pseudo classes, :active or :focus, that address this, but the JavaScript needs to be able to add or remove an active class too.

#gallery__filters {
  margin: 2em 1em;
  text-align: center;
}

.gallery__filter {
  outline: none;
  cursor: pointer;
  color: #eb3924;
  text-decoration: none;
  margin: 5px 10px;
  padding: 10px;
  background: none;
  transition: .3s ease all;
  border: 2px solid #fff;
  border-radius: 6px;
  /* H4 */
  font-family: 'Playfair Display,' serif;
  font-weight: 900;
  font-size: 1.333em;
  line-height: 1.2;
}

.gallery__filter:hover {
  color: #2c3739;
  background-color: #ebeded;
}

.gallery__filter.tag--active {
  background-color: #fff;
  border: 2px solid #eb3924;
}

5. Style the error message.

Add a bright color and background color to help highlight the error message. Leave it showing by default. JavaScript will hide the message when it executes, at which point the message is moot anyway.

#gallery__filters__message {   color: #eb3924;   background-color: rgba(235, 209, 143, .6);   padding: 10px; }

6. Style the thumbnail images with CSS.

Style the columns, image wrappers, and image captions much like the earlier tutorial, “How to Code a CSS-Only Image Gallery with Animated Effects,” with different names. Also style a CSS class for the active thumbnail images, which show on screen. This is the class JS adds or removes based on the filters. I manually added this CSS class to the images to display them by default, as opposed to the original W3Schools settings.

The full CSS document now looks like:

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

.clearfix::after {
  content: "";
  clear: both;
  display: table;
}

#gallery__filters {
  margin: 2em 1em;
  text-align: center;
}

.gallery__filter {
  outline: none;
  cursor: pointer;
  color: #eb3924;
  text-decoration: none;
  margin: 5px 10px;
  padding: 10px;
  background: none;
  transition: .3s ease all;
  border: 2px solid #fff;
  border-radius: 6px;
  /* H4 */
  font-family: "Playfair Display", "Times Bold", Times, "Times New Roman Bold", "Times New Roman", serif;
  font-weight: 900;
  font-size: 1.333em;
  line-height: 1.2;
}

.gallery__filter:hover {
  color: #2c3739;
  background-color: #ebeded;
}

.gallery__filter.tag--active {
  background-color: #fff;
  border: 2px solid #eb3924;
}

#gallery__filters__message {
  color: #eb3924;
  background-color: rgba(235, 209, 143, .6);
  padding: 10px;
}

.gallery__row {
  display: table;
  border-collapse: collapse;
  text-align: center;
  margin: 0 auto;
  padding: 0;
  width: 100%;
}

@media screen and (min-width: 200px) {
  .gallery__column {
    float: left;
    width: 50%;
    display: none;
    /* Hide gallery__row by default */
  }
}

@media screen and (min-width: 680px) {
  .gallery__column {
    width: 25%;
  }
}

#gallery__thumb {
  background-color: white;
  padding: 10px;
  box-sizing: border-box;
}

#gallery__thumbs .thumb__wrapper {
  position: relative;
  overflow: hidden;
  float: left;
  box-sizing: border-box;
  width: 100%;
  /* Height and padding-bottom creates a fluid height - here set to a 3x2 aspect ratio */
  height: 0;
  padding-bottom: 66%;
}

#gallery__thumbs .thumb__wrapper .thumb__caption {
  left: -100%;
  top: 0;
  width: 100%;
  height: 100%;
  background: rgba(44, 55, 57, .9);
  text-align: center;
  position: absolute;
  z-index: 100;
  padding: 20% 5%;
  box-sizing: border-box;
  transition: all 300ms ease;
  /* H3 */
  font-family: "Montserrat", "Trebuchet MS", Helvetica, sans-serif;
         font-weight: 500;
  font-size: 1em;
  line-height: 1.2;
  color: #fff;
}

#gallery__thumbs .thumb__wrapper div {
  width: 100%;
  height: 100%;
  transition: all 300ms ease;
  backface-visibility: hidden;
  /* Required for fluid heights */
  position: absolute;
  background-size: contain !important;
}

#gallery__thumbs .thumb__wrapper div:hover,
#gallery__thumbs .thumb__wrapper div:focus,
#gallery__thumbs .thumb__wrapper div:active {
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  transform: scale(1.4);
}

#gallery__thumbs .thumb__wrapper div:hover .thumb__caption,
#gallery__thumbs .thumb__wrapper div:focus .thumb__caption,
#gallery__thumbs .thumb__wrapper div:active .thumb__caption {
  transform: none;
  transform: scale(.72) translateX(139%);
}

#gallery__thumbs .thumb__wrapper #thumb--townhouse {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-lara-lee-townhouse-app.png) center;
}

#gallery__thumbs .thumb__wrapper #thumb--smile {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-smile-team.jpg) center;
}

#gallery__thumbs .thumb__wrapper #thumb--adelante {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-adelante-shoe-co-packaging.png) center;
}

#gallery__thumbs .thumb__wrapper #thumb--turkey {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-spc-turkey-trot-logo-on-map.png) center;
}

#gallery__thumbs .thumb__wrapper #thumb--open {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-open-heart-parrot-rescue-logo-design-t-shirt.jpg) center;
}

#gallery__thumbs .thumb__wrapper #thumb--lawn {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-spc-lawn-enforcement-logo.jpg) center;
}

#gallery__thumbs .thumb__wrapper #thumb--safety {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-spc-safety-and-security.jpg) center;
}

#gallery__thumbs .thumb__wrapper #thumb--golf {
  background: url(https://www.laralee.design/wp-content/uploads/2020/09/blog-20180719-filterable-gallery-afcea-nova-golf-tournament-visual-identity.jpg) center;
}

.filter--show {
  display: block;
}

7. Begin working the JS. Hide the error message immediately on page load.

Call the error message by its ID and re-style its CSS to hide it.
getElementById("gallery__filters__message").style.display = "none";

8. Store a JS function filters the images based on their CSS tags.

Name a function filterSelection(c) with parameter “c.” Passing parameter “c” stores a representation of an element’s collection of CSS classes.

Inside the function, first gather all the thumbnail image elements by their parent container’s CSS class, “gallery__column,” and store that group as a variable for later reference.

It’s important to note here that .getElementsByClassName() returns an array, a list of elements. That list may have only a single element, yet the result JS returns is not that element but the list. To target each element, a loop is necessary. An array appended by a number [0] will select a single element in that array, here the first one.

To automatically go through each item without spelling out document.getElementsByClassName(gallery__column)[0], document.getElementsByClassName(gallery__column)[1], document.getElementsByClassName(gallery__column)[2], etc., simply replace the number with the index variable storing the current list item the loop is on in the array: document.getElementsByClassName(gallery__column)[i].

Inside the loop, write removeFilter(thumbnails[i], "filter--show"); to run a function removeFilter, which we haven’t defined yet.

Beneath this, a shorthand If statement:
if (thumbnails[i].className.indexOf(c) > -1) addFilter(thumbnails[i], "filter--show");
to loop through each element and run a function addFilter, which also hasn’t been defined yet.

Afterward, close the function without initializing it. For now it’s enough to simply define it.

Here’s this step coded out with commentary:

//Store a function that loops through .process-step element and shows it
//The filterSelection() function looks for the CSS class named in the parameter, here variable "c"
function filterSelection(c) {
  var thumbnails = document.getElementsByClassName("gallery__column");
  if (c == "all") c = "";
  // Add the "filter--show" class (display:block) to the filtered thumbnails, and remove the "filter--show" class from the elements that are not selected
  for (var i = 0; i < thumbnails.length; i++) {
    removeFilter(thumbnails[i], "filter--show");
    if (thumbnails[i].className.indexOf(c) > -1) addFilter(thumbnails[i], "filter--show");
  }
}

9. Store a JS function that adds the designated CSS class to trigger showing an image with “.filter—show.”

Name a function addFilter(element, name) with two parameters, element and name.

Element and name become variables other functions, namely filterSelection(), uses to store which HTML element and CSS class is targeted.

For example, the line addFilter(thumbnails[i], "filter--show") from filterSelection() clarifies then “element” represents document.getElementsByClassName("gallery__column")[i] and “name” represents the CSS class name, here “.filter--show.” However, the name could be any filter entered as parameter “c”, like .filter--logos or .filter--packaging.

Inside the function, define a variable:
var arr1 = element.className.split(" ");
that saves an array listing all the CSS classes a thumbnail element has (since CSS classes are listed w/spaces in between in HTML).

Define a second variable:
var arr2 = name.split(" ");
that saves an array of number of elements with the .filter--show class as instances of how many times .filter--show occurs.

For example, clicking the “Show All” button will log eight (8) instances of .filter--show classes, one for each thumbnail image in the gallery.

Beneath the variable declarations, add a loop that runs as many times as there are instances of the designated filter (.filter--logos, .filter--identity, etc.) and adds the CSS class .filter--show to trigger its image display:

  for (var i = 0; i < arr2.length; i++) {
    while (arr1.indexOf(arr2[i]) > -1) {
      arr1.splice(arr1.indexOf(arr2[i]), 1);
    }
  }

The final function is shown below:

// Store a function that shows filtered elements
function addFilter(element, name) {
  //Used in filterSelection() as addFilter(thumbnails[i], "filter--show")
  //Then element is document.getElementsByClassName("gallery__column")[i]
  //Then name is CSS class .filter--show in the above example; filterSelection("filter--logos") would find all logo elements
  //Saves an array listing all the CSS classes an element has (since CSS classes are listed w/spaces in between in HTML)
  var arr1 = element.className.split(" ");
  //Saves an array of number of elements with the .filter--show class as instances of how many times .filter--show occurs
  var arr2 = name.split(" ");
  //Loops as many times as there are instances of .filter--show
  for (var i = 0; i < arr2.length; i++) {
    // .indexOF() searches for the occurence of .filter--show in arr2; result of -1 means there are 0 occurrences
    //If the list of CSS classes includes 0 occurrences of .filter--show, then add .filter--show to that element
    if (arr1.indexOf(arr2[i]) == -1) {
      element.className += " " + arr2[i];
    }
  }
}

10. Store a JS function that hides an image without the designated CSS class by removing “.filter—show” from the CSS class list.

Similar to function addFilter() above, define a second function, function removeFilter():

// Store a function that hides elements that are not selected
function removeFilter(element, name) {
  var arr1 = element.className.split(" ");
  var arr2 = name.split(" ");
  for (var i = 0; i < arr2.length; i++) {
    // As long as the CSS class list has ANY occurrences of .filter--show...
    while (arr1.indexOf(arr2[i]) > -1) {
      // ...remove the latest occurrence of .filter--show in the CSS class list (removes 1 at a time)
      arr1.splice(arr1.indexOf(arr2[i]), 1);
    }
  }
  // Re-join the ammended CSS class list back to the HTML element
  element.className = arr1.join(" ");
}

11. Ensure all thumbnail images are showing on page load.

Add a line in the JavaScript to run filterSelection("all"). As a result, this code adds the .filter--show CSS class to every thumbnail image.

filterSelection("all");

12. Highlight the active filter that the user clicked/clicks.

Create a loop that goes through every filter button, listening for a mouse click. Then define a function that removes the .tag--active CSS class from the default button (our fallback option, “Show All”) or whatever the old button was, and instead assign the .tag--active class to the new button.

var gallery__filterContainer = document.getElementById("gallery__filters");
var gallery__filters = gallery__filterContainer.getElementsByClassName("gallery__filter");
for (var i = 0; i < gallery__filters.length; i++) {
  //Listen for a mouse click on each
  gallery__filters[i].addEventListener("click", function() {
    //If one registers a click then remove the CSS class .tag--active from the old button and give it instead to the new button that registered the click
    var current = document.getElementsByClassName("tag--active");
    current[0].className = current[0].className.replace(" tag--active", "");
    this.className += " tag--active";
  });
}

13. Finally, add the JS code that actually runs the filterSelection() based on whichever button/tag was selected.

Although I could have written a loop that would go through each filter button on the DOM, and write If Else statements to single out which filter to run for which button, I nevertheless think hard-coding it this way reduces the lines of code and makes it simple.

document.getElementById('filter--show-all').addEventListener("click", function() {
  filterSelection("all");
}, false);

document.getElementById('filter--logos').addEventListener("click", function() { 
  filterSelection("tag--logos");
}, false);

document.getElementById('filter--identity').addEventListener("click", function() {
  filterSelection("tag--identity");
}, false);

document.getElementById('filter--illustration').addEventListener("click", function() {
  filterSelection("tag--illustration");
}, false);

document.getElementById('filter--web-development').addEventListener("click", function() { 
  filterSelection("tag--web-development");
}, false);

document.getElementById('filter--packaging').addEventListener("click", function() {
  filterSelection("tag--packaging");
}, false);

14. Here’s all the vanilla JavaScript, altogether:

//Hide "JavaScript disabled" error message when JavaScript loads
document.getElementById("gallery__filters__message").style.display = "none";

//Store a function that loops through .process-step element and shows it
//The filterSelection() function looks for the CSS class named in the parameter, here variable "c"
function filterSelection(c) {
  var thumbnails = document.getElementsByClassName("gallery__column");
  if (c == "all") c = "";
  // Add the "filter--show" class (display:block) to the filtered thumbnails, and remove the "filter--show" class from the elements that are not selected
  for (var i = 0; i < thumbnails.length; i++) {
    removeFilter(thumbnails[i], "filter--show");
    if (thumbnails[i].className.indexOf(c) > -1) addFilter(thumbnails[i], "filter--show");
  }
}

// Store a function that shows filtered elements
function addFilter(element, name) {
  //Used in filterSelection() as addFilter(thumbnails[i], "filter--show")
  //Then element is document.getElementsByClassName("gallery__column")[i]
  //Then name is CSS class .filter--show in the above example; filterSelection("filter--logos") would find all logo elements
  //Saves an array listing all the CSS classes an element has (since CSS classes are listed w/spaces in between in HTML)
  var arr1 = element.className.split(" ");
  //Saves an array of number of elements with the .filter--show class as instances of how many times .filter--show occurs
  var arr2 = name.split(" ");
  //Loops as many times as there are instances of .filter--show
  for (var i = 0; i < arr2.length; i++) {
    // .indexOF() searches for the occurence of .filter--show in arr2; result of -1 means there are 0 occurrences
    //If the list of CSS classes includes 0 occurrences of .filter--show, then add .filter--show to that element
    if (arr1.indexOf(arr2[i]) == -1) {
      element.className += " " + arr2[i];
    }
  }
}

// Store a function that hides elements that are not selected
function removeFilter(element, name) {
  var arr1 = element.className.split(" ");
  var arr2 = name.split(" ");
  for (var i = 0; i < arr2.length; i++) {
    // As long as the CSS class list has ANY occurrences of .filter--show...
    while (arr1.indexOf(arr2[i]) > -1) {
      // ...remove the latest occurrence of .filter--show in the CSS class list (removes 1 at a time)
      arr1.splice(arr1.indexOf(arr2[i]), 1);
    }
  }
  // Re-join the ammended CSS class list back to the HTML element
  element.className = arr1.join(" ");
}

//On page load, show all thumbnails
filterSelection("all");

// Highlight the filter currently active
var gallery__filterContainer = document.getElementById("gallery__filters");
var gallery__filters = gallery__filterContainer.getElementsByClassName("gallery__filter");
// For as many filter buttons are found within the parent container...
for (var i = 0; i < gallery__filters.length; i++) {
  //Listen for a mouse click on each
  gallery__filters[i].addEventListener("click", function() {
    //If one registers a click then remove the CSS class .tag--active from the old button and give it instead to the new button that registered the click
    var current = document.getElementsByClassName("tag--active");
    current[0].className = current[0].className.replace(" tag--active", "");
    this.className += " tag--active";
  });
}

//Run a query function when a button is clicked
document.getElementById('filter--show-all').addEventListener("click", function() {
  filterSelection("all");
}, false);
document.getElementById('filter--logos').addEventListener("click", function() {
  filterSelection("tag--logos");
}, false);
document.getElementById('filter--identity').addEventListener("click", function() {
  filterSelection("tag--identity");
}, false);
document.getElementById('filter--illustration').addEventListener("click", function() {
  filterSelection("tag--illustration");
}, false);
document.getElementById('filter--web-development').addEventListener("click", function() {
  filterSelection("tag--web-development");
}, false);
document.getElementById('filter--packaging').addEventListener("click", function() {
  filterSelection("tag--packaging");
}, false);

Final HTML, CSS, & JavaScript for the Filterable Image Gallery:

You may simply copy the final HTML, CSS, and JavaScript from each section above. Alternatively, for live manipulation, check out the JSFiddle below.

<script async src="//jsfiddle.net/laralee/0s35n8yk/embed/"></script>

Overall, the final HTML, CSS, and JS altogether looks like this. Enjoy your new interactive, filterable gallery!