Challenge: Image gallery
In this challenge, we'll get you to build a fairly common item you'll see on a lot of websites — a JavaScript-powered image gallery. Along the way, you'll be tested on your knowledge of loops, functions, conditionals, events, DOM scripting, and object basics.
In this article
Starting point
To begin, click thePlay button in one of the code panels below to open the provided example in the MDN Playground. You'll then follow the instructions in theProject brief section to complete the JavaScript functionality.
The HTML looks like so:
<h1>Image gallery example</h1><div> <img src="https://mdn.github.io/shared-assets/images/examples/learn/gallery/pic1.jpg" alt="Closeup of a human eye" /> <div></div> <button>Darken</button></div><div></div>The starting JavaScript looks like this:
const displayedImage = document.querySelector(".displayed-img");const thumbBar = document.querySelector(".thumb-bar");const btn = document.querySelector("button");const overlay = document.querySelector(".overlay");body { font-family: sans-serif; width: 640px; margin: 0 auto; background-color: lightgray;}h1 { text-align: center;}.full-img { position: relative; display: block; width: 640px; height: 480px; margin-bottom: 2px;}.overlay { position: absolute; top: 0; left: 0; width: 640px; height: 480px;}button { background: rgb(150 150 150 / 0.6); border: 1px solid #999999; position: absolute; cursor: pointer; top: 2px; left: 2px;}button:hover,button:focus { color: rgb(150 150 150 / 1); background-color: black;}.thumb-bar { display: flex; gap: 2px; cursor: pointer;}.thumb-bar img { display: block; width: 100px; flex: 1;}.thumb-bar img:hover,.thumb-bar img:focus { outline: 2px solid blue;}We've hidden the gallery CSS for brevity, but you can see it when you look at the app in the MDN Playground.
Project brief
You have been provided with some HTML, CSS, and a few lines of JavaScript code. Your job is to follow the instructions below, writing the necessary JavaScript to turn this into a working image gallery.
The gallery will consist of a large image and a row of thumbnails. When a thumbnail is clicked or tabbed to andEnter/Return then pressed, that thumb should then display as the large image. The relevant<img> element should also be updated with the correctalt text.
In the top-left corner there is a button that, when repeatedly pressed, toggles the large image between a darker and lighter tint, achieved by changing the transparency of a<div> element that has been overlaid on top of the large image.
The images you need to embed in the example and their requiredalt text are as follows:
Create a data object
First of all, we'd like you to declare an array of objects calledimages. Each object should contain two properties:
filename: The name of the image file (not the full URL).alt: The image'salttext.
Add the images to the thumbnail bar
Next, we want you to loop through theimages and use some DOM scripting to embed them all on the page via<img> elements. They should be included as children of the<div> element with the class ofthumb-bar, which we've already referenced in thethumbBar constant.
- Create a constant called
baseURLcontaining the base URL of each image file (all of the URL not including the filename). - Create a
for ... ofloop to loop through theimages. - For each image, create a new
<img>element. - Set the
<img>source to equal the URL of the image, which should be a combination of thebaseURLand thefilename, and thealtattribute equal to thealttext. - Add another attribute to the
<img>to make it focusable via the keyword. - Append the
<img>to thethumbBar. - Add a
clickevent handler to the<img>so that when it is clicked, a function calledupdateDisplayedImage()is run, which displays the clicked image at full size. You'll create this function later on. - Add another event handler to the
<img>so that once it is focused via the keyboard, the clicked image can be displayed at full size by pressing theEnter/Return key (and no other key). This is a stretch goal that will take a bit of research to figure out.
Create theupdateDisplayedImage() function
Now it's time to create the function to display an activated thumbnail at full size. We've stored a reference to the full size<img> element in thedisplayedImage constant.
- Define the
updateDisplayedImage()function. - Inside the function body, set the
displayedImagesource to equal the source of the<img>that was activated. - Set the
displayedImagealt text to equal the alt text of the<img>that was activated.
Wire up the Darken/Lighten button
We've stored a reference to the "Darken/Lighten"<button> in thebtn constant, and a reference to the transparent<div> we have overlaid on top of the full size<img> in theoverlay constant. We'd like you to:
- Add a
clickevent handler to the<button>with an anonymous function set as the handler function. - Inside the function body, add a conditional structure that tests whether the
<button>has aclassset on it ofdarkor not. - If the
<button>has aclassofdarkwhen clicked, change its text content toLighten, and change theoverlayelement's background color torgb(0 0 0 / 0.5). Remove the<button>element'sdarkclass. - If the
<button>does not have aclassofdarkwhen clicked, change its text content toDarken, and change theoverlayelement's background color torgb(0 0 0 / 0). Add the<button>element'sdarkclass. - Can you think of a way to toggle the
darkclass using a single line of code, run after the conditional structure? This is another stretch goal, but give it a go.
Hints and tips
- You don't need to change the HTML or CSS.
Example
Your finished app should work like the following live example:
Click here to show the solution
The finished JavaScript should look something like this:
const displayedImage = document.querySelector(".displayed-img");const thumbBar = document.querySelector(".thumb-bar");const btn = document.querySelector("button");const overlay = document.querySelector(".overlay");// Solution: Create a data objectconst images = [ { filename: "pic1.jpg", alt: "Closeup of a human eye" }, { filename: "pic2.jpg", alt: "Rock that looks like a wave" }, { filename: "pic3.jpg", alt: "Purple and white pansies" }, { filename: "pic4.jpg", alt: "Section of wall from a pharaoh's tomb" }, { filename: "pic5.jpg", alt: "Large moth on a leaf" },];// Solution: Loop through the images// Create a baseURL constant containing the baseURL of the imagesconst baseURL = "https://mdn.github.io/shared-assets/images/examples/learn/gallery/";// Loop through the images using a for...of loopfor (const image of images) { // Create a new image element const newImage = document.createElement("img"); // Set the source and alt text for the image newImage.src = `${baseURL}${image.filename}`; newImage.alt = image.alt; // Make the image focusable via the keyboard newImage.tabIndex = "0"; // Append the image as a child of the thumbBar thumbBar.appendChild(newImage); // Update the display to show the image full size when a thumb is clicked newImage.addEventListener("click", updateDisplayedImage); // Update the display to show the image full size when the "Enter" key // is pressed after it has been focused newImage.addEventListener("keydown", (e) => { if (e.code === "Enter") { updateDisplayedImage(e); } });}// Solution: Create the updateDisplayedImage() functionfunction updateDisplayedImage(e) { displayedImage.src = e.target.src; displayedImage.alt = e.target.alt;}// Solution: Wire up the Darken/Lighten button// Add a click event listener on the buttonbtn.addEventListener("click", () => { // If the button has a "dark" class set, // change text to "Lighten" and make the overlay darker if (btn.classList.contains("dark")) { btn.textContent = "Lighten"; overlay.style.backgroundColor = "rgb(0 0 0 / 0.5)"; } else { // Else, change text to "Darken" and make // the overlay lighter btn.textContent = "Darken"; overlay.style.backgroundColor = "rgb(0 0 0 / 0)"; } // Toggle the class ready for the next button press btn.classList.toggle("dark");});