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.

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’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.

Each image needs tags for the filters; CSS classes that collectively group them into columns and thumbnails; wrappers for the hover effects; and spans for the titles that appear when the hover effects execute. The images will sit against each other using floats, so also add the clearfix CSS class to the parent container.

The full HTML looks like this:

<iframe src="//jsfiddle.net/laralee/b764hwou/embedded/html/" width="100%" height="600" frameborder="0" allowfullscreen="allowfullscreen"></figure></iframe>

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:

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.

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);
    }
  }

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():

function removeFilter(element, name) {
  var arr1 = element.className.split(" ");
  var arr2 = name.split(" ");
  for (var i = 0; i < arr2.length; i++) {
    while (arr1.indexOf(arr2[i]) > -1) {
      arr1.splice(arr1.indexOf(arr2[i]), 1);
    }
  }
  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++) {
  gallery__filters[i].addEventListener("click", function() {
    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);

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

<script async src="{embedSrc}"></script>

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