javascriptarrayscaptionpicturegallery

How to make captions in a gallery of images based on the text content of other divs in JavaScript?


I have a gallery of images with some information appearing when you hover on each of them. When you click on each image, the full size is shown. I want to add a caption under the full-size versions based on the the information that appears when you hover on them.

For that purpose, I have made an array of caption texts. However, I am not able to figure out how I can assign the related caption to the respective image. I would also like to remove paragraph spacing from the caption.

To clarify, I added some images.

The texts that you see when you hover. Not the issue here. enter image description here

The caption under the zoomed image. My Question: enter image description here

Here is my code:

// Modal Images
var modal = document.getElementById("modalContainer");
var modalImg = document.getElementById("modalImg");
var imgArr = document.querySelectorAll(".img-container .img");
var currentIndex;
imgArr.forEach(function(img, i) {
  img.onclick = function() {
    var backgroundImage = img.style.backgroundImage
      .slice(4, -1)
      .replace(/"/g, "");
    modal.style.display = "block";
    modalImg.src = backgroundImage;
    currentIndex = i;
  };
});

// caption
var captionArr = [];
var captionInfo = document.querySelectorAll(".img-info");
var caption = document.getElementById("caption");
for (var i = 0; i < captionInfo.length; i++) {
  var current = captionInfo[i];
  console.log(current.textContent);
}
caption.innerHTML = captionArr[i];
console.log(captionArr);

// previous and next buttons
var prev = document.querySelector(".prev");
var next = document.querySelector(".next");

next.addEventListener("click", nextImage);
modalImg.addEventListener("click", nextImage);

function nextImage() {
  if (currentIndex < imgArr.length - 1) {
    currentIndex++;
    modalImg.src = imgArr[currentIndex].style.backgroundImage
      .slice(4, -1)
      .replace(/"/g, "");
  }
}

prev.addEventListener("click", previousImage);

function previousImage() {
  if (currentIndex > 0) {
    currentIndex--;
    modalImg.src = imgArr[currentIndex].style.backgroundImage
      .slice(4, -1)
      .replace(/"/g, "");
  }
}

// close the modal
var span = document.getElementsByClassName("close")[0];

span.addEventListener("click", close);

function close() {
  modal.style.display = "none";
}
.close {
  position: absolute;
  top: 15px;
  right: 35px;
  color: #f1f1f1;
  font-size: 40px;
  font-weight: bold;
  transition: 0.3s;
}

.close:hover,
.close:focus {
  color: #bbb;
  text-decoration: none;
  cursor: pointer;
}

.img {
  display: block;
  width: 200px;
  height: 100%;
  margin-right: 10px;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  cursor: pointer;
}

.img-info {
  display: block;
  opacity: 0;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 15px !important;
  max-width: 80%;
}

.img-container:hover {
  .img:after {
    content: "";
    width: 100%;
    height: 100%;
    position: absolute;
    background-color: rgba(0, 0, 0, 0.6);
  }
  .img-info {
    opacity: 1;
  }
}

.frame {
  display: flex;
  height: 100px;
  min-height: 20rem;
}

.modal {
  display: none;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  overflow: auto;
  background-color: rgb(0, 0, 0);
  background-color: rgba(0, 0, 0, 0.9);
  justify-content: center;
  align-items: center;
}

.modal-content {
  margin: auto;
  display: block;
  width: 80%;
  max-width: 700px;
  max-height: 80%;
  object-fit: contain;
  -o-object-fit: contain;
  animation-name: zoom;
  animation-duration: 0.6s;
}


/* Next & previous buttons */

.prev,
.next {
  cursor: pointer;
  position: absolute;
  top: 50%;
  -moz-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
  width: auto;
  padding: 0.8rem;
  color: #777;
  font-weight: normal;
  font-size: 18px;
  transition: 0.6s ease;
  border-radius: 0 3px 3px 0;
  user-select: none;
  -webkit-user-select: none;
  z-index: 3;
}

.prev:hover,
.next:hover {
  color: #000;
}

.next {
  right: 0;
}

.prev {
  left: 0;
}

@keyframes zoom {
  from {
    transform: scale(0)
  }
  to {
    transform: scale(1)
  }
}
<div class="frame">
  <div class="img-container">
    <div class="img" style="background-image: url(https://images.unsplash.com/photo-1501159771943-cc9027db4d8b?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80)">
    </div>
    <div class="img-info">
      <div class="img-info-title">
        <h3>
          Some text 1...
        </h3>
      </div>
      <div class="img-info-subtitle">
        <h4>
          The rest 1...
        </h4>
      </div>
    </div>
  </div>
  <div class="img-container">
    <div class="img" style="background-image: url(https://images.unsplash.com/photo-1509042239860-f550ce710b93?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80">
    </div>
    <div class="img-info">
      <div class="img-info-title">
        <h3>
          Some text 2...
        </h3>
      </div>
      <div class="img-info-subtitle">
        <h4>
          The rest 2...
        </h4>
      </div>
    </div>
  </div>
  <div class="img-container">
    <div class="img" style="background-image: url(https://images.unsplash.com/photo-1583936410736-a4af4f3013d6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80)">
    </div>
    <div class="img-info">
      <div class="img-info-title">
        <h3>
          Some text 3...
        </h3>
      </div>
      <div class="img-info-subtitle">
        <h4>
          The rest 3...
        </h4>
      </div>
    </div>
  </div>
</div>

<!-- The Modal -->
<div id="modalContainer" class="modal">
  <!-- Buttons -->
  <span class="close">&times;</span>
  <a class="prev">&#10094;</a>
  <a class="next">&#10095;</a>
  <!-- Modal Content -->
  <img id="modalImg" class="modal-content">
  <div class="caption">
    <p id="caption"></p>
  </div>
</div>
</div>


Solution

  • The first thing you can do is select the h3 and h4 and trim each of them to remove whitespace like:

    console.log(current.querySelector('h3').textContent.trim() + " " + current.querySelector('h4').textContent.trim() );
    

    // caption
    var captionArr = [];
    var captionInfo = document.querySelectorAll(".img-info");
    var caption = document.getElementById("caption");
    for (var i = 0; i < captionInfo.length; i++) {
      var current = captionInfo[i];
      var current_str = current.querySelector('h3').textContent.trim() + " " + current.querySelector('h4').textContent.trim();
      console.log(current_str);
      captionArr.push(current_str);
    }
    
    // Modal Images
    var modal = document.getElementById("modalContainer");
    var modalImg = document.getElementById("modalImg");
    var imgArr = document.querySelectorAll(".img-container .img");
    var currentIndex;
    imgArr.forEach(function(img, i) {
      img.onclick = function() {
        var backgroundImage = img.style.backgroundImage
          .slice(4, -1)
          .replace(/"/g, "");
        modal.style.display = "block";
        modalImg.src = backgroundImage;
        currentIndex = i;
        console.log('i',i);
        caption.innerHTML = captionArr[i];
      };
    });
    
    // previous and next buttons
    var prev = document.querySelector(".prev");
    var next = document.querySelector(".next");
    
    next.addEventListener("click", nextImage);
    modalImg.addEventListener("click", nextImage);
    
    function nextImage() {
      if (currentIndex < imgArr.length - 1) {
        currentIndex++;
        modalImg.src = imgArr[currentIndex].style.backgroundImage
          .slice(4, -1)
          .replace(/"/g, "");
        caption.innerHTML = captionArr[currentIndex];    
      }
    }
    
    prev.addEventListener("click", previousImage);
    
    function previousImage() {
      if (currentIndex > 0) {
        currentIndex--;
        modalImg.src = imgArr[currentIndex].style.backgroundImage
          .slice(4, -1)
          .replace(/"/g, "");
        caption.innerHTML = captionArr[currentIndex];  
      }
    }
    
    // close the modal
    var span = document.getElementsByClassName("close")[0];
    
    span.addEventListener("click", close);
    
    function close() {
      modal.style.display = "none";
    }
    .img-info h3,  .img-info h3,{
      color: #f1f1f1;
      opacity: 1 !important;
    }
    
    .close {
      position: absolute;
      top: 15px;
      right: 35px;
      color: #f1f1f1;
      font-size: 40px;
      font-weight: bold;
      transition: 0.3s;
    }
    
    .close:hover,
    .close:focus {
      color: #bbb;
      text-decoration: none;
      cursor: pointer;
    }
    
    .img {
      display: block;
      width: 200px;
      height: 100%;
      margin-right: 10px;
      background-size: cover;
      background-position: center;
      background-repeat: no-repeat;
      cursor: pointer;
    }
    
    .img-info {
      display: block;
      /*opacity: 0;*/
      position: absolute;
      /*top: 50%;*/
      /*transform: translateY(-50%);*/
      /*left: 15px !important;*/
      max-width: 80%;
    }
    
    .img-container:hover {
      .img:after {
        content: "";
        width: 100%;
        height: 100%;
        position: absolute;
        background-color: rgba(0, 0, 0, 0.6);
      }
      .img-info {
        opacity: 1;
      }
    }
    
    .frame {
      display: flex;
      height: 100px;
      min-height: 20rem;
    }
    
    .modal {
      display: none;
      position: fixed;
      z-index: 1;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      box-sizing: border-box;
      overflow: auto;
      background-color: rgb(0, 0, 0);
      background-color: rgba(0, 0, 0, 0.9);
      justify-content: center;
      align-items: center;
    }
    
    .modal-content {
      margin: auto;
      display: block;
      width: 80%;
      max-width: 700px;
      max-height: 80%;
      object-fit: contain;
      -o-object-fit: contain;
      animation-name: zoom;
      animation-duration: 0.6s;
    }
    
    
    /* Next & previous buttons */
    
    .prev,
    .next {
      cursor: pointer;
      position: absolute;
      top: 50%;
      -moz-transform: translateY(-50%);
      -ms-transform: translateY(-50%);
      transform: translateY(-50%);
      width: auto;
      padding: 0.8rem;
      color: #777;
      font-weight: normal;
      font-size: 18px;
      transition: 0.6s ease;
      border-radius: 0 3px 3px 0;
      user-select: none;
      -webkit-user-select: none;
      z-index: 3;
    }
    
    .prev:hover,
    .next:hover {
      color: #000;
    }
    
    .next {
      right: 0;
    }
    
    .prev {
      left: 0;
    }
    
    @keyframes zoom {
      from {
        transform: scale(0)
      }
      to {
        transform: scale(1)
      }
    }
    <div class="frame">
      <div class="img-container">
        <div class="img" style="background-image: url(https://images.unsplash.com/photo-1501159771943-cc9027db4d8b?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80)">
        </div>
        <div class="img-info">
          <div class="img-info-title">
            <h3>
              Some text 1...
            </h3>
          </div>
          <div class="img-info-subtitle">
            <h4>
              The rest 1...
            </h4>
          </div>
        </div>
      </div>
      <div class="img-container">
        <div class="img" style="background-image: url(https://images.unsplash.com/photo-1509042239860-f550ce710b93?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80">
        </div>
        <div class="img-info">
          <div class="img-info-title">
            <h3>
              Some text 2...
            </h3>
          </div>
          <div class="img-info-subtitle">
            <h4>
              The rest 2...
            </h4>
          </div>
        </div>
      </div>
      <div class="img-container">
        <div class="img" style="background-image: url(https://images.unsplash.com/photo-1583936410736-a4af4f3013d6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80)">
        </div>
        <div class="img-info">
          <div class="img-info-title">
            <h3>
              Some text 3...
            </h3>
          </div>
          <div class="img-info-subtitle">
            <h4>
              The rest 3...
            </h4>
          </div>
        </div>
      </div>
    </div>
    
    <!-- The Modal -->
    <div id="modalContainer" class="modal">
      <!-- Buttons -->
      <span class="close">&times;</span>
      <a class="prev">&#10094;</a>
      <a class="next">&#10095;</a>
      <!-- Modal Content -->
      <img id="modalImg" class="modal-content">
      <div class="caption">
        <p id="caption" align="center" id="caption" style="color:  white;"></p>
      </div>
    </div>
    </div>

    Note, I modified the CSS, removing a lot of what you had:

    .img-info {
      display: block;
      /*opacity: 0;*/
      position: absolute;
      /*top: 50%;*/
      /*transform: translateY(-50%);*/
      /*left: 15px !important;*/
      max-width: 80%;
    }
    

    Then we get the following:

    enter image description here

    I believe this is the desired behaviour.

    Now to add under the model pop-up too I think.

    To do this we need to modify the JS a bit (we need to create the captioArr first and push each string of text onto it. Then the index of that array matches the index of your forEach loop and so we can use caption.innerHTML = captionArr[i];):

    // caption
    var captionArr = [];
    var captionInfo = document.querySelectorAll(".img-info");
    var caption = document.getElementById("caption");
    for (var i = 0; i < captionInfo.length; i++) {
      var current = captionInfo[i];
      var current_str = current.querySelector('h3').textContent.trim() + " " + current.querySelector('h4').textContent.trim();
      console.log(current_str);
      captionArr.push(current_str);
    }
    
    // Modal Images
    var modal = document.getElementById("modalContainer");
    var modalImg = document.getElementById("modalImg");
    var imgArr = document.querySelectorAll(".img-container .img");
    var currentIndex;
    imgArr.forEach(function(img, i) {
      img.onclick = function() {
        var backgroundImage = img.style.backgroundImage
          .slice(4, -1)
          .replace(/"/g, "");
        modal.style.display = "block";
        modalImg.src = backgroundImage;
        currentIndex = i;
        console.log('i',i);
        caption.innerHTML = captionArr[i];
      };
    });
    

    And then the style of the p tag a bit (it needs to be white and centred to see it)

    <div class="caption">
      <p id="caption" align="center" id="caption" style="color:  white;"></p>
    </div>
    

    And then we get:

    enter image description here

    Finally, to change the caption when we click the previous and next buttons add one line to the prevImage and nextImage functions:

    function nextImage() {
      if (currentIndex < imgArr.length - 1) {
        currentIndex++;
        modalImg.src = imgArr[currentIndex].style.backgroundImage
          .slice(4, -1)
          .replace(/"/g, "");
        caption.innerHTML = captionArr[currentIndex];    
      }
    }
    
    function previousImage() {
      if (currentIndex > 0) {
        currentIndex--;
        modalImg.src = imgArr[currentIndex].style.backgroundImage
          .slice(4, -1)
          .replace(/"/g, "");
        caption.innerHTML = captionArr[currentIndex];  
      }
    }
    

    Namely, caption.innerHTML = captionArr[currentIndex];