Back to Freecodecamp

Build a Lightbox Viewer

curriculum/challenges/english/blocks/lab-lightbox-viewer/66db57ad34c7089b9b41bfd6.md

latest11.4 KB
Original Source

--description--

A lightbox is used on websites to showcase multimedia content in a more engaging way through use of a popup or modal window over the page's main content.

In this lab, you will create a lightbox gallery that displays full-size images when a thumbnail is clicked. For each image, two versions are provided: a thumbnail and a full-size image. The full-size image is the same as the thumbnail, but without the -thumbnail suffix.

Objective: Fulfill the user stories below and get all the tests to pass to complete the lab.

User Stories:

  1. You should have a div with a class of gallery within your body.

  2. Within the .gallery element, you should have three image thumbnails, each with a class of gallery-item. You should use the following links for thumbnail images:

    • https://cdn.freecodecamp.org/curriculum/labs/stonehenge-thumbnail.jpg
    • https://cdn.freecodecamp.org/curriculum/labs/storm-thumbnail.jpg
    • https://cdn.freecodecamp.org/curriculum/labs/trees-thumbnail.jpg
  3. You should have a div with a class of lightbox within your body.

  4. You should have a button with an id of close-btn within your .lightbox element. You can use × as its text if you want.

  5. You should have a img with an id of lightbox-image within your .lightbox element.

  6. Your .lightbox element should have a fixed position so that the preview opens on top of the current images.

  7. Your .lightbox element should cover the entire viewport by setting the height and width to 100% of the container. You should ensure that the .lightbox element starts at the top left corner of the container.

  8. .lightbox should have a background color. Initially, its display property should be set to none to hide it.

  9. When you click one of your .gallery-item elements, the .lightbox element's display property should be set to flex to make the .lightbox element and the two elements within it visible.

  10. When you click one of your .gallery-item elements, the #lightbox-image element's src should be set to a full-size version of the image clicked by removing -thumbnail from the image's src attribute. The full-size images are located at the following links:

    • https://cdn.freecodecamp.org/curriculum/labs/stonehenge.jpg
    • https://cdn.freecodecamp.org/curriculum/labs/storm.jpg
    • https://cdn.freecodecamp.org/curriculum/labs/trees.jpg
  11. When your .lightbox element is visible and you click the #close-btn or the .lightbox element, the .lightbox element's display should be set back to none.

Note: Be sure to link your stylesheet and the JavaScript file in your HTML.

--hints--

You should have a div with the class of gallery inside your body element.

js
assert.equal(document.querySelector("body .gallery")?.tagName, "DIV");

Within the .gallery element, you should have three img elements with the class of gallery-item.

js
assert.lengthOf(document.querySelectorAll(".gallery .gallery-item"), 3)

Within the .gallery element, you should have an img element with the src set to https://cdn.freecodecamp.org/curriculum/labs/stonehenge-thumbnail.jpg.

js
const images = document.querySelectorAll(".gallery .gallery-item");
let srcCount = 0; 
const targetSrc = "https://cdn.freecodecamp.org/curriculum/labs/stonehenge-thumbnail.jpg";

for (let image of images) {
  if (image.src === targetSrc) { 
    srcCount++; 
  }
}

assert.strictEqual(srcCount, 1);

Within the .gallery element, you should have an img element with the src set to https://cdn.freecodecamp.org/curriculum/labs/storm-thumbnail.jpg.

js
const images = document.querySelectorAll(".gallery .gallery-item");
let srcCount = 0; 
const targetSrc = "https://cdn.freecodecamp.org/curriculum/labs/storm-thumbnail.jpg";

for (let image of images) {
  if (image.src === targetSrc) { 
    srcCount++; 
  }
}

assert.strictEqual(srcCount, 1);

Within the .gallery element, you should have an img element with the src set to https://cdn.freecodecamp.org/curriculum/labs/trees-thumbnail.jpg.

js
const images = document.querySelectorAll(".gallery .gallery-item");
let srcCount = 0; 
const targetSrc = "https://cdn.freecodecamp.org/curriculum/labs/trees-thumbnail.jpg";

for (let image of images) {
  if (image.src === targetSrc) { 
    srcCount++; 
  }
}

assert.strictEqual(srcCount, 1);

You should have a div element with the class of lightbox inside your body element.

js
const lightboxEl = document.querySelector("body .lightbox");
assert.exists(lightboxEl);
assert.equal(lightboxEl?.tagName, "DIV");

Within your .lightbox element, you should have a button element with the id set to close-btn.

js
assert.equal(document.querySelector(".lightbox #close-btn")?.tagName, "BUTTON");

Within your .lightbox element, you should have an img element with the id set to lightbox-image.

js
assert.equal(document.querySelector(".lightbox #lightbox-image")?.tagName, "IMG");

Your .lightbox element should have fixed positioning.

js
assert.equal(new __helpers.CSSHelp(document).getStyle(".lightbox")?.position, "fixed");

Your .lightbox element should cover the entire viewport.

js
const style = new __helpers.CSSHelp(document).getStyle(".lightbox");

const width = style?.width;
const height = style?.height;

assert.oneOf(
  width,
  ["100%", "100vw"]
);

assert.oneOf(
  height,
  ["100%", "100vh"]
);

Your .lightbox element should be aligned with top left corner of the container.

js
assert.equal(new __helpers.CSSHelp(document).getStyle(".lightbox")?.top, "0px");
assert.equal(new __helpers.CSSHelp(document).getStyle(".lightbox")?.left, "0px");

Your .lightbox element should have a background color.

js
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle(".lightbox")?.backgroundColor);

Your .lightbox element should be hidden initially.

js
assert.equal(new __helpers.CSSHelp(document).getStyle(".lightbox")?.display, "none");

When you click one of your .gallery-item elements, the .lightbox element's display property should be set to flex to make .lightbox and the two elements within it visible.

js
const lightbox = document.querySelector(".lightbox");

function getComputedDisplay(element) {
  return window.getComputedStyle(element).display;
}

assert.strictEqual(getComputedDisplay(lightbox), "none");

const galleryItem = document.querySelector(".gallery-item");
galleryItem.dispatchEvent(new Event("click"));

assert.strictEqual(getComputedDisplay(lightbox), "flex");

When you click one of your .gallery-item elements, the #lightbox-image element's src should be set to a full-size version of the image clicked by removing -thumbnail from the image's src attribute.

js
const galleryItems = document.querySelectorAll(".gallery-item");
const lightboxImage = document.getElementById("lightbox-image");

assert.isAbove(galleryItems.length, 0);

function getFullSizeImageUrl(thumbnailUrl) {
  return thumbnailUrl.replace("-thumbnail", "");
}

galleryItems.forEach((item) => {
  const thumbnailUrl = item.src; 
  const expectedFullSizeUrl = getFullSizeImageUrl(thumbnailUrl);

  item.dispatchEvent(new MouseEvent("click", { bubbles: true }));

  assert.strictEqual(lightboxImage.src, expectedFullSizeUrl);
});

When your .lightbox element is visible and you click the #close-btn button, the .lightbox element's display should be set back to none.

js
const lightbox = document.querySelector(".lightbox");
const closeBtn = document.getElementById("close-btn"); 
const galleryItem = document.querySelector(".gallery-item");

function getComputedDisplay(element) {
  return window.getComputedStyle(element).display;
}

galleryItem.dispatchEvent(new Event("click", { bubbles: true }));

assert.strictEqual(getComputedDisplay(lightbox), "flex");

closeBtn.dispatchEvent(new Event("click", { bubbles: true }));

assert.strictEqual(getComputedDisplay(lightbox), "none");

When your .lightbox element is visible and you click the .lightbox element, the .lightbox element's display should be set back to none.

js
const lightbox = document.querySelector(".lightbox");

function getComputedDisplay(element) {
  return window.getComputedStyle(element).display;
}

const galleryItem = document.querySelector(".gallery-item");
galleryItem.dispatchEvent(new Event("click", { bubbles: true }));

assert.strictEqual(getComputedDisplay(lightbox), "flex");

lightbox.dispatchEvent(new Event("click", { bubbles: true }));

assert.strictEqual(getComputedDisplay(lightbox), "none");

--seed--

--seed-contents--

html
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lightbox Viewer</title>
  </head>

  <body>

  </body>

</html>
css
js

--solutions--

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lightbox Viewer</title>
    <link rel="stylesheet" href="styles.css">
  </head>

  <body>
    <h1 class="title">Museum Gallery</h1>

    <div class="gallery">
      
      
      
    </div>
    <div id="lightbox" class="lightbox">
      <button id="close-btn" class="close-btn">&times;</button>
      
    </div>
    <script src="script.js"></script>
  </body>
</html>
css
body {
  margin: 0;
  padding: 0;
  background-color: #f0f0f0;
  color: #333;
}

.title {
  margin-top: 100px;
  text-align: center;
  background-color: rgb(211, 221, 224);  
}

.gallery {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  padding: 20px;
}

.gallery-item {
  width: 250px;
  height: 300px;
  object-fit: cover;
  cursor: pointer;
  transition: transform 0.2s;
}

.gallery-item:hover {
  transform: scale(1.05);
}

.lightbox {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.8);
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.lightbox img {
  max-width: 90%;
  max-height: 90%;
}

.close-btn {
  position: absolute;
  top: 20px;
  right: 10px;
  font-size: 2em;
  color: white;
  cursor: pointer;
  border: none;
  background-color: transparent;
}
js
const lightbox = document.getElementById("lightbox");
const lightboxImage = document.getElementById("lightbox-image");

function openLightbox(src) {
  lightboxImage.src = src;
  lightbox.style.display = "flex"; 
}

function closeLightbox() {
  lightbox.style.display = "none"; 
}

const closeButton = document.getElementById("close-btn");

closeButton.addEventListener("click", closeLightbox);

lightbox.addEventListener("click", (e) => {
  if (e.target === lightbox) {
    closeLightbox(); 
  }
});

const galleryItems = document.querySelectorAll(".gallery-item");

galleryItems.forEach((item) => {
  item.addEventListener("click", () => {
    openLightbox(item.src.replace("-thumbnail", "")); 
  });
});