How to Create a Portfolio Gallery with Filtering by Categories
Image Gallery
How To, for Front End and Web Applications
By: Chrysanthus Date Published: 30 Apr 2025
A portfolio is a set of samples of work. This tutorial explains How to Create a Portfolio Gallery with Filtering, by Categories. For this project, there are three categories, which are: nature, cars, and people. Each category has three images; each image with its title and work description. When the webpage loads (for the first time), all three categories are displayed. Nature is the first, in a row with three elements. Cars is the second, in a row with three elements. People is the third and last, in a row, with three elements.
Above the rows are four buttons, in a short row. The first is Show-All. The second is Nature. The third is Cars. And the fourth and last is People. When Show-All is clicked, all the three categories (three rows) are shown. When Nature is clicked, the row of three elements for nature, appears just below the buttons, without the other rows. When Cars is clicked, the row of three elements for cars, appears just below the buttons (without the other rows). When People is clicked, the row of three elements for people, appears just below the buttons (without the other rows).
The complete code for this project is given at the end of this tutorial. Copy all the code into a text editor. Save the file with any name, but with the extension, .html . Open the saved file in the browser. Click all the buttons, to see the effect of each. The images are in the broad-network.com website. So the reader has to be hooked on to the Internet. The reader is advised to do that, before continuing to read the rest of this tutorial.
The Webpage Skeleton Code
The skeleton code for the webpage, to which more useful code will be added is:
<!DOCTYPE html>
<html>
<head>
<title>Filtering Portfolio Gallery</title>
<link rel='icon' href='https://www.examplee.com/images/logo.jpg' type='image/x-icon'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Description of the Web Page.">
<style>
</style>
<script type="text/javascript">
</script>
</head>
<body onload="filterSelection('all')">
<script type="text/javascript">
</script>
</body>
</html>
Any responsible modern webpage should have at least the first four tags in the HTML Head element above. The HTML style element will have all the essential CSS rules. JavaScript is in two parts: one part in the head element; and the other part, in the body element. The images and their work descriptions are in div elements, which are in the body element.
The HTML body Element Content
The content of the body element is:
<!-- MAIN (Center website) -->
<div class="main">
<h1>myLogo.com</h1>
<hr>
<h2>Portfolio</h2>
<div id="myBtnContainer">
<button class="btn active" onclick="filterSelection('all')"> Show All</button>
<button class="btn" onclick="filterSelection('nature')"> Nature</button>
<button class="btn" onclick="filterSelection('cars')"> Cars</button>
<button class="btn" onclick="filterSelection('people')"> People</button>
</div>
<!-- Portfolio Gallery Grid -->
<div class="row">
<div class="column nature">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/mountains.jpg" alt="Mountains" style="width:100%">
<h4>Mountains</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column nature">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/lights.jpg" alt="Lights" style="width:100%">
<h4>Lights</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column nature">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/nature.jpg" alt="Nature" style="width:100%">
<h4>Forest</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="row">
<!-- similar to the above row -->
</row>
<div class="row">
<!-- similar to the above row -->
</div>
<!-- END MAIN -->
</div>
<script type="text/javascript">
</script>
There is one div that has the whole document. The class-name for this overall div is "main" . The four buttons are inside an inner div, whose ID is "myBtnContainer". Buttons are inline-level elements. So the buttons will all appear in one horizontal (short) line.
Each row is an inner div, with the class-name, "row". Each inner div has three innermost divs, each with the class-name, "column nature" or "column cars" or "column people". The three innermost divs are for three works. Each innermost div has the fourth level inner div, with class-name, "content".
Each content div has the image tag, the H4 element for the work title, and the paragraph element for the work description.
The HTML Style Element Content
Apart from the attribute, "style='width:100%' ", all the styles are in the CSS style-sheet (HTML style element). This 100% is relative to the fourth level inner div (content div).
At the top within the style element content, is the CSS rule:
* {
box-sizing: border-box;
}
This is called the box-sizing property. The asterisk, * in front of the rule, means that it is applied to all the containers, such as the div element. The box-sizing property means that the width of each element is measured from the left edge of the left border, to the right edge of the opposite border, instead of from the left edge of the element's content, to the right edge of the element's content. The box-sizing property also means that the height of each element is measured from the top edge of the top border, to the bottom edge of the opposite border, instead of from the top edge of the element's content, to the bottom edge of the element's content. The box-sizing property makes coding convenient for projects like this one.
Styling the body Element
The CSS rule for the body element is:
body {
background-color: #f1f1f1;
padding: 20px;
font-family: Arial;
}
The background color of the whole webpage is #f1f1f1 (light gray). The padding (gap between content and border) for the four sides is 20px. The font to be used by all the elements in the body, including the body itself, is Arial.
Styling the Overall Container
If the reader had tested the complete code at a wide screen, he/she would have noticed that the document is centered horizontally. That is, there is quite a gap from the left edge of the page to the left edge of the document; and the same gap exists from the right edge of the document, to the right edge of the page. The CSS rule for the overall container div is:
div.main {
max-width: 1000px;
margin: auto;
}
The maximum width is 1000px, not enough to cover the width of a wide screen. "margin: auto;" means that the browser chooses the size of the four margins.
Styling the H1 Element
The CSS rule for the H1 element is:
h1 {
font-size: 50px;
word-break: break-all;
}
The font size is 50px. With "word-break: break-all;", any word that is longer than the width of the H1 element, is broken at the right end, and wrapped to the next line.
Styling row divs
A row div has the three inner divs. Its CSS rule is:
div.row {
margin: 10px -16px;
}
The top and bottom margin is 10px and the left and right margin is -16px (minus sixteen pixels). If the 10px is absent, then there will be no vertical separation between the row divs, with the body background color. The -16px actually takes a row div margin outwards. Remember that the box-sizing property is applicable here (the whole webpage). In this way, the left and right edges of the row div content align approximately well with the left and right edges of the document header content (horizontal rule, for example).
Row and Cell Padding
The CSS rule for those is:
div.row, div.row > .column {
padding: 8px;
}
The padding values for cell divs and row div have been chosen such that there is alignment of the vertical edges of the row divs and the document header content.
Styling the column divs
The CSS rule for the column (cell) divs in a row is:
.column {
float: left;
width: 33.33%;
display: none; /* Hide all elements by default */
}
Note that the selector is ".column" and not "div.column" . This is because, "column" is just part of some class-names. The div class-names in the body element with "column" are: "column nature", "column cars" and "column people" .
The divs are all floated left, to form a row at the wide screen. This floating effect has to be stopped, otherwise, there will be serious issues at the display (browser). Since there are three divs per row, each with its own inner div and image, the width of each div is 33.33% of the width of the row div (100% divided by 3 equals 33.33%). The display is none. When they are needed, JavaScript will make the display, block; see below.
Stopping the Floating Effect of the column divs
In order to stop the floating effect, one feature is needed: A somewhat fourth row div (just after the third) is needed. This is a pseudo element with the name, "after". The pseudo element is of a CSS rule (within the start and end tags of the HTML style element); it is:
div.row:after {
content: "";
display: table;
clear: both;
}
Note how the selector has been expressed, which includes the class-name, "row". "content: ''; " as CSS property in the pseudo element, gives the pseudo element, no content. "display: table;" as CSS property in the pseudo element, turns the pseudo element, into an empty table. A table by default, is a block-level element, meaning that it will be displayed on the next line, below the mimicked row. "clear: both;" as CSS property in the pseudo element, stops both left and right floating of the pseudo element.
Styling the Fourth Level Inner div
The fourth level inner div of class-name, "content", has the image, the title of the work, and the description of the work. Its CSS rule is:
div.content {
background-color: white;
padding: 10px;
}
Its background color is white, and there is a padding of 10px for the four sides.
Styling Each Button
The CSS rule for each button is:
.btn {
border: none;
padding: 12px 16px;
background-color: white;
cursor: pointer;
}
No button has a border. The top and bottom padding for each button is 12px; and the left and right padding is 16px. The background color is white. "cursor: pointer;" means that, when the mouse pointer goes over a button, it should become a hand. It is fashionable to have it like that today.
Note that the selector is ".btn" and not "button.btn" . This is because, the first button in the body element has the attribute, "class='btn active' ".
Button On-hover
The CSS rule for that is:
.btn:hover {
background-color: #ddd;
}
When the mouse pointer goes over any button, its background color becomes #ddd (light gray).
Active Button
The button whose row is displayed (after clicking), is referred to as the active button. The CSS rule for that is:
.active {
background-color: #666;
color: white;
}
After clicking, when the mouse pointer is removed from the button, the background color becomes #666 (dark gray); and the color of text becomes white.
The show CSS Rule
The show CSS rule is:
div.show {
display: block;
}
There is no element in the HTML body, with the attribute, "class='show'" . In other words, there is no element in the HTML body with the class-name, "show". This show CSS rule remains only in memory, and it can be applied to any appropriate element in the HTML body. This CSS rule has only one property, which is "display: block;" . JavaScript applies this CSS rule when the button is clicked, to show the row div (to make the display property of its inner elements, block). It does this by including the show CSS rule into what is known as the classList of the row divs.
JavaScript
Each row in the HTML body has three nested divs, with class-name, "column nature" or "column cars" or "column people". When a row is to be displayed, the display property of its corresponding three nested divs, is made block, one-by-one. When a row is not to be displayed, the display property of its corresponding three nested divs, is made none, one-by-one.
The reader might ask, "Why not display and hide each row, instead of displaying and hiding the row elements?" - This is because of the float effect of the row elements.
There are two JavaScript scripts. There is one in the body element after the divs, and there is another in the head element. Both scripts respond to the click of each of the buttons. In the body element, the code for the buttons is:
<div id="myBtnContainer">
<button class="btn active" onclick="filterSelection('all')"> Show All</button>
<button class="btn" onclick="filterSelection('nature')"> Nature</button>
<button class="btn" onclick="filterSelection('cars')"> Cars</button>
<button class="btn" onclick="filterSelection('people')"> People</button>
</div>
Each button has the onclick event. When a button is clicked, it calls the filterSelection() function in the JavaScript in the head element. That function will display its row. The first button displays all the rows, by sending the argument, 'all' to the filterSelection() function. The second button displays only the nature row, by sending the argument, 'nature' to the filterSelection() function. The third button displays only the cars row, by sending the argument, 'cars' to the filterSelection() function. The fourth button displays only the people row, by sending the argument, 'people' to the filterSelection() function. The JavaScript in the head element is:
<script type="text/javascript">
function filterSelection(c) {
var x, i;
x = document.getElementsByClassName("column");
if (c == "all")
c = "";
for (i = 0; i < x.length; i++) {
removeClass(x[i], "show");
if (x[i].className.indexOf(c) > -1)
addClass(x[i], "show");
}
}
function addClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
if (arr1.indexOf(arr2[i]) == -1)
{element.className += " " + arr2[i];}
}
}
function removeClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
while (arr1.indexOf(arr2[i]) > -1) {
arr1.splice(arr1.indexOf(arr2[i]), 1);
}
}
element.className = arr1.join(" ");
}
</script>
The filterSelection() function calls the addClass() and removeClass() functions. Read through the above code.
The button whose row (or rows) is displayed, is said to be the active button. While its row is displayed, the button has to be highlighted. The JavaScript in the body element uses the addEventListener() method. The purpose of the script is to make a button active. The script is:
<script type="text/javascript">
// Add active class to the current button (highlight it)
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function(){
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
</script>
Note from the previous JavaScript, that each of the buttons already has an onclick event, to call the filterSelection() function in the head element. And so, to add another onclick event, the addEventListener() method is necessary. So, each button has two onclick event. This script in the body element, removes the "active" class-name from the button that had it, and then put it in the button that has just been clicked. The click addEventListener() method is also called, every time a button is clicked. Read through the code.
Webpage On-load
When the webpage is loaded (for the first time), the filterSelection() function with the argument, 'all' is called, and all the rows are displayed. If this is not done, no row will be displayed, when the webpage loads. To achieve the display of all rows, put the onload="filterSelection('all')" event at the start tag of the body element. The onload event is called, just after the webpage has completely loaded.
The Complete Webpage Code
And there you have it: the complete webpage code is:
<!DOCTYPE html>
<html>
<head>
<title>Filtering Portfolio Gallery</title>
<link rel='icon' href='https://www.examplee.com/images/logo.jpg' type='image/x-icon'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Description of the Web Page.">
<style>
* {
box-sizing: border-box;
}
body {
background-color: #f1f1f1;
padding: 20px;
font-family: Arial;
}
/* Center website */
div.main {
max-width: 1000px;
margin: auto;
}
h1 {
font-size: 50px;
word-break: break-all;
}
div.row {
margin: 10px -16px;
}
/* Add padding BETWEEN each column */
div.row, div.row > .column {
padding: 8px;
}
/* Create three equal columns (cells) that floats next to each other */
.column {
float: left;
width: 33.33%;
display: none; /* Hide all elements by default */
}
/* Clear floats after rows */
div.row:after {
content: "";
display: table;
clear: both;
}
/* Content */
div.content {
background-color: white;
padding: 10px;
}
/* Style the buttons */
.btn {
border: none;
padding: 12px 16px;
background-color: white;
cursor: pointer;
}
.btn:hover {
background-color: #ddd;
}
.active {
background-color: #666;
color: white;
}
/* The "show" class is added to the filtered elements */
.show {
display: block;
}
</style>
<script type="text/javascript">
function filterSelection(c) {
var x, i;
x = document.getElementsByClassName("column");
if (c == "all")
c = "";
for (i = 0; i < x.length; i++) {
removeClass(x[i], "show");
if (x[i].className.indexOf(c) > -1)
addClass(x[i], "show");
}
}
function addClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
if (arr1.indexOf(arr2[i]) == -1)
{element.className += " " + arr2[i];}
}
}
function removeClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
while (arr1.indexOf(arr2[i]) > -1) {
arr1.splice(arr1.indexOf(arr2[i]), 1);
}
}
element.className = arr1.join(" ");
}
</script>
</head>
<body onload="filterSelection('all')">
<!-- MAIN (Center website) -->
<div class="main">
<h1>myLogo.com</h1>
<hr>
<h2>Portfolio</h2>
<div id="myBtnContainer">
<button class="btn active" onclick="filterSelection('all')"> Show All</button>
<button class="btn" onclick="filterSelection('nature')"> Nature</button>
<button class="btn" onclick="filterSelection('cars')"> Cars</button>
<button class="btn" onclick="filterSelection('people')"> People</button>
</div>
<!-- Portfolio Gallery Grid -->
<div class="row">
<div class="column nature">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/mountains.jpg" alt="Mountains" style="width:100%">
<h4>Mountains</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column nature">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/lights.jpg" alt="Lights" style="width:100%">
<h4>Lights</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column nature">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/nature.jpg" alt="Nature" style="width:100%">
<h4>Forest</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="row">
<div class="column cars">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/cars1.jpg" alt="Car" style="width:100%">
<h4>Retro</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column cars">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/cars2.jpg" alt="Car" style="width:100%">
<h4>Fast</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column cars">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/cars3.jpg" alt="Car" style="width:100%">
<h4>Classic</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
</row>
<div class="row">
<div class="column people">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/people1.jpg" alt="Car" style="width:100%">
<h4>Girl</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column people">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/people2.jpg" alt="Car" style="width:100%">
<h4>Man</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<div class="column people">
<div class="content">
<img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/people3.jpg" alt="Car" style="width:100%">
<h4>Woman</h4>
<p>Lorem ipsum dolor..</p>
</div>
</div>
<!-- END GRID -->
</div>
<!-- END MAIN -->
</div>
<script type="text/javascript">
// Add active class to the current button (highlight it)
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function(){
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
</script>
</body>
</html>
Copy all the code into a text editor. Save the file with any name, but with the extension, .html . Open the saved file in the browser. Click all the buttons, to see the effect of each. The images are in the broad-network.com website. So the reader has to be hooked on to the Internet.
Chrys