The DOM part 2
We will now build a webpage to show off our favorite animals, complete with filters to easily find what we're looking for. Say we have bears, dolphins, ducks, and frogs, clicking the bear filter will hide the rest, only showing bears.
There's also a search box. Type in "dolphin water," and only images with dolphin and water in their alt attribute, like "Dolphin on Body of Water" will show up.
You can also use both filters together. Search "green" and click the frog filter, and you'll only see the "green frog" images.
Step 1
Create a form with a search input, 4 radio buttons (one for each animal) and one more labelled Show All” that is checked by default.
Set the value on each radio button, eg. value="frog". For the show all radio button: value="all"
<h1>Animal Filtering</h1>
<form id="filterAnimals" class="animal-form">
<fieldset class="animal-form__fieldset">
<legend class="animal-form__legend">Animal Type</legend>
<div class="animal-form__radio-group">
<div class="animal-form__radio-container">
<input type="radio" name="animalName" id="bear" value="bear" class="animal-form__radio-input">
<label for="bear" class="animal-form__label">Bear</label>
</div>
<div class="animal-form__radio-container">
<input type="radio" name="animalName" id="dolphin" value="dolphin" class="animal-form__radio-input">
<label for="dolphin" class="animal-form__label">Dolphin</label>
</div>
<div class="animal-form__radio-container">
<input type="radio" name="animalName" id="duck" value="duck" class="animal-form__radio-input">
<label for="duck" class="animal-form__label">Duck</label>
</div>
<div class="animal-form__radio-container">
<input type="radio" name="animalName" id="frog" value="frog" class="animal-form__radio-input">
<label for="frog" class="animal-form__label">Frog</label>
</div>
<div class="animal-form__radio-container">
<input type="radio" name="animalName" id="all" value="all" class="animal-form__radio-input" checked>
<label for="all" class="animal-form__label">Show All</label>
</div>
</div>
</fieldset>
<div class="animal-form__search-container">
<label for="search" class="animal-form__label--search">Search</label>
<input type="search" name="searchAnimals" id="searchAnimals" class="animal-form__search-input">
</div>
</form>
Check out this live example on CodePen.
Step 2
Add 16 images from pexels.com in a random order. For each image, include a data-animal attribute (data- attributes are a way to store extra information on HTML elements, used for storing custom data private to the page or application) to specify the type of animal it represents, for example, data-animal="frog".
Add an imageFilter class to each image.
<section class="animals-container">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/158109/kodiak-brown-bear-adult-portrait-wildlife-158109.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Brown Bear on a Body of Water" data-animal="bear">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/35435/pexels-photo.jpg?auto=compress&cs=tinysrgb&h=350"
alt="Brown Bear" data-animal="bear">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/1068554/pexels-photo-1068554.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Close-Up Photography of Grizzly Bear" data-animal="bear">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/598966/pexels-photo-598966.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Grayscale Photo of a Polar Bear Cub " data-animal="bear">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/64219/dolphin-marine-mammals-water-sea-64219.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Cute Dolphine Underwater" data-animal="dolphin">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/1986374/pexels-photo-1986374.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Gray Dolphin on Body of Water" data-animal="dolphin">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/225869/pexels-photo-225869.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Adorable Dolphins on Surface of Water" data-animal="dolphin">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/162079/dolphin-sea-marine-mammals-wise-162079.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Dolphin's Head in the Surface" data-animal="dolphin">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/162140/duckling-birds-yellow-fluffy-162140.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Duckling on Black Soil during Daytime" data-animal="duck">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/660266/pexels-photo-660266.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Depth of Field Photography of Mallard Duck on Body of Water" data-animal="duck">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/209035/pexels-photo-209035.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Brown White and Blue Duck" data-animal="duck">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/833687/pexels-photo-833687.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Green and Gray Mallard Duck" data-animal="duck">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/70083/frog-macro-amphibian-green-70083.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Green Frog" data-animal="frog">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/76957/tree-frog-frog-red-eyed-amphibian-76957.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Green Blue Yellow and Orange Frog on Green Leaf" data-animal="frog">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/45863/frog-butterfly-pond-mirroring-45863.jpeg?auto=compress&cs=tinysrgb&h=350"
alt="Macro Photography of Green Frog" data-animal="frog">
<img class="imageFilter" loading="lazy"
src="https://images.pexels.com/photos/35669/hyla-meridionalis-the-frog-amphibians.jpg?auto=compress&cs=tinysrgb&h=350"
alt="Shallow Focus Photography of Green Frog" data-animal="frog">
</section>
Check out this live example on CodePen.
Step 3 - Using JavaScript
Add a variable animalRadios that holds all the animal radio buttons.
Add a variable images that holds all images with the imageFilter class.
Loop through each animal radio button and add a change event listener that calls a function filterAnimals.
Add a class of hidden in CSS with display: none
// Add a animalRadios variable.
const animalRadios = document.querySelectorAll('.animal-form input[name="animalName"]');
// Add a images variable.
const images = document.querySelectorAll('.imageFilter');
// Add change event listener to radio buttons.
animalRadios.forEach(radio => radio.addEventListener('change', filterAnimals));
/* Add hidden class to css */
.hidden {
display: none;
}
Check out this live example on CodePen.
Step 4
In the filterAnimals function loop through each item in the images array.
If the selected animal is all remove the class hidden from the image.
If the selected animal is frog, add the hidden class if the image isn’t a frog; otherwise remove it.
// Define a function called filterAnimals
const filterAnimals = () => {
// Select the currently selected animal from the radio buttons
const selectedAnimal = document.querySelector(
'.animal-form input[name="animalName"]:checked'
).value;
// Loop through each image element
images.forEach(img => {
// Check if the selected animal matches the data-animal attribute of the current image
const animalMatch = selectedAnimal === 'all' || img.dataset.animal === selectedAnimal;
// If the animal matches, add the 'hidden' class to the image, then remove it after 50 milliseconds
if (animalMatch) {
img.classList.add('hidden');
setTimeout(() => {
img.classList.remove('hidden');
}, 50);
}
// If the animal does not match, simply add the 'hidden' class to the image
else {
img.classList.add('hidden');
}
});
};
Check out this live example on CodePen.
Step 5
Add a keyup event listener to the search element.
The text entered should be used to filter based on the image alt attribute.
The selected animal radio button should also be taken into account.
Pressing enter shouldn’t submit the form.
// Add search input variable
const searchInput = document.querySelector('#searchAnimals');
// Add form variable
const animalForm = document.querySelector('.animal-form');
// Define a function called filterAnimals
const filterAnimals = () => {
// Get the search term from the search input, remove any leading or trailing whitespace, and convert to lowercase
const searchTerm = searchInput.value.trim().toLowerCase();
// Get the value of the currently selected animal from the radio buttons
const selectedAnimal = document.querySelector(
'.animal-form input[name="animalName"]:checked'
).value;
// Loop through each image element
images.forEach(img => {
// Get the alt text of the current image, convert to lowercase
const altText = img.alt.toLowerCase();
// Split the search term into an array of individual terms
const searchTerms = searchTerm.split(' ');
// Check if every term in the search term array is included in the alt text of the image
const searchMatch = searchTerms.every(term => altText.includes(term));
// Check if the selected animal matches the data-animal attribute of the current image
const animalMatch =
selectedAnimal === 'all' || img.dataset.animal === selectedAnimal;
// If both the search term and the selected animal match, add the 'hidden' class to the image and remove it after 50 milliseconds
if (searchMatch && animalMatch) {
img.classList.add('hidden');
setTimeout(() => {
img.classList.remove('hidden');
}, 50);
}
// If either the search term or the selected animal does not match, add the 'hidden' class to the image
else {
img.classList.add('hidden');
}
});
};
// Add key up event listener to search element.
searchInput.addEventListener('keyup', () => {
if (!searchInput.value.trim()) {
return;
}
filterAnimals();
});
// Prevent form submission
animalForm.addEventListener('submit', e => e.preventDefault());
Check out this live example on CodePen.
Step 6
Add some helper text above the images that says something like “showing animals that match the search “{searchString}” and the filter "{filter}"
Inform the user that there are no results when nothing matches
<!-- Add elements to display helper text above image container-->
<p class="animal__filter-text"></p>
<p class="animal__no-result">No results</p>
// Get elements
const filterTextElement = document.querySelector('.animal__filter-text');
const noResultText = document.querySelector('.animal__no-result');
// Define a function to generate the helper text based on search term and selected animal filter
const showHelperText = (searchTerm, selectedAnimal) => {
let filterText = 'Showing all animals'; // Default text for showing all animals
// Update text based on search term and selected animal filter
if (searchTerm && selectedAnimal !== 'all') {
filterText += ` that match the search "${searchTerm}" and the filter ${selectedAnimal}`;
} else if (searchTerm) {
filterText += ` that match the search "${searchTerm}"`;
} else if (selectedAnimal !== 'all') {
filterText += ` with the filter ${selectedAnimal}`;
}
return filterText; // Return the generated filter text
};
// Define a function to display "No results" text if imagesCount is 0, otherwise hide it
const showNoResultText = imagesCount => {
if (imagesCount === 0) {
noResultText.style.display = 'block'; // Display "No results" text
} else {
noResultText.style.display = ''; // Hide "No results" text
}
};
// Define a function to filter animals based on search term and selected animal filter
const filterAnimals = () => {
const searchTerm = searchInput.value.trim().toLowerCase(); // Get the search term from the search input
const selectedAnimal = document.querySelector(
'.animal-form input[name="animalName"]:checked'
).value; // Get the value of the currently selected animal filter
let imagesCount = 0; // Initialize counter for matching images
filterTextElement.textContent = showHelperText(searchTerm, selectedAnimal); // Display helper text
// Loop through each image element
images.forEach(img => {
const altText = img.alt.toLowerCase(); // Get the alt text of the image and convert to lowercase
const searchTerms = searchTerm.split(' '); // Split the search term into an array of individual terms
const searchMatch = searchTerms.every(term => altText.includes(term)); // Check if all search terms are included in the alt text
const animalMatch =
selectedAnimal === 'all' || img.dataset.animal === selectedAnimal; // Check if the image matches the selected animal filter
// If both search term and animal filter match, add the 'hidden' class to the image
// and increment imagesCount to keep track of matched images
if (searchMatch && animalMatch) {
img.classList.add('hidden');
// Use setTimeout to briefly hide the image before removing the 'hidden' class
// This is done to trigger the CSS transition for smoother visibility change
setTimeout(() => {
img.classList.remove('hidden');
}, 50);
// Increment imagesCount to keep track of matched images
imagesCount++;
} else {
img.classList.add('hidden'); // Otherwise, add the 'hidden' class to the image
}
});
showNoResultText(imagesCount); // Display "No results" text if imagesCount is 0
};
Check out this live example on CodePen.