Как создать галерею с возможностью просмотра полноразмерного изображения в модальном окне?

420
15 августа 2021, 03:20

Я создала галерею с изображениями.Мне необходимо: 1) динамически создать элементы галереи по указанному шаблону; 2)При клике по элементу галереи должно открываться модальное окно с полноразмерным изображением. Обязательно использовать делегирование событий и слушай клики на элементе ul.gallery; 3)Модальное окно должно закрываться по клику на кнопку button[data-action="close-modal"], по клику на div.overlay или по нажатию ESC. Разметка элемента галереи должна быть:

<li class="gallery__item"> 
  <a 
    class="gallery__link" 
    href="https://cdn.pixabay.com/photo/2010/12/13/10/13/tulips-2546_1280.jpg" 
  > 
    <img 
      class="gallery__image" 
      src="https://cdn.pixabay.com/photo/2010/12/13/10/13/tulips-2546__340.jpg" 
      data-source="https://cdn.pixabay.com/photo/2010/12/13/10/13/tulips-2546_1280.jpg" 
      alt="Tulips" 
    /> 
 
    <span class="gallery__icon"> 
      <i class="material-icons">zoom_out_map</i> 
    </span> 
  </a> 
</li>

Ссылка на оригинальное изображение должна храниться в data-атрибуте source на элементе img, и указываться в href ссылки. Вот мой код что у меня получилось:

'use strict'; 
const images = [ 
  { 
    preview: 
      'https://cdn.pixabay.com/photo/2019/05/14/16/43/hokkaido-4202825__340.jpg', 
    original: 
      'https://cdn.pixabay.com/photo/2019/05/14/16/43/hokkaido-4202825_1280.jpg', 
    description: 'Hokkaido Flower', 
  }, 
  { 
    preview: 
      'https://cdn.pixabay.com/photo/2019/05/14/22/05/container-4203677__340.jpg', 
    original: 
      'https://cdn.pixabay.com/photo/2019/05/14/22/05/container-4203677_1280.jpg', 
    description: 'Container Haulage Freight', 
  }, 
  { 
    preview: 
      'https://cdn.pixabay.com/photo/2019/05/16/09/47/view-4206785__340.jpg', 
    original: 
      'https://cdn.pixabay.com/photo/2019/05/16/09/47/view-4206785_1280.jpg', 
    description: 'Aerial Beach View', 
  }, 
  ]; 
  const menu = document.querySelector('ul.gallery'); 
menu.classList.add('gallery__link'); 
const markUp = createImages(images); 
menu.insertAdjacentHTML('afterbegin', markUp); 
function createImages(images) { 
  return images.reduce((acc, image) => (acc += createMarkup(image)), ' '); 
} 
function createMarkup({ preview, original, description }) { 
  const itemList = `<li><img src='${(preview, 
  original)}' alt='${description}'></li>`; 
  return itemList; 
}
html { 
  box-sizing: border-box; 
} 
 
*, 
*::before, 
*::after { 
  box-sizing: inherit; 
} 
 
body { 
  margin: 0; 
  font-family: sans-serif; 
  background-color: #fff; 
  color: #212121; 
} 
 
.gallery { 
  display: grid; 
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); 
  grid-gap: 16px; 
  max-width: 1440px; 
  padding: 0; 
  margin: 0; 
  list-style: none; 
  margin-left: auto; 
  margin-right: auto; 
} 
 
.gallery__item { 
  position: relative; 
  box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2), 
    0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 2px 1px -1px rgba(0, 0, 0, 0.12); 
} 
 
.gallery__image { 
  display: block; 
  height: 100%; 
  width: 100%; 
  object-fit: cover; 
} 
 
.gallery__link { 
  display: block; 
  text-decoration: none; 
  height: 100%; 
} 
 
.gallery__icon { 
  position: absolute; 
  top: 50%; 
  left: 50%; 
  transform: translate(-50%, -50%); 
  display: flex; 
  align-items: center; 
  justify-content: center; 
  width: 56px; 
  height: 56px; 
  color: #fff; 
  cursor: pointer; 
  transition: opacity 200ms linear; 
  opacity: 0; 
  pointer-events: none; 
} 
.gallery__icon i { 
  font-size: 48px; 
} 
 
.gallery__item:hover .gallery__icon { 
  opacity: 1; 
} 
 
.gallery__image { 
  transition: transform 200ms ease-in-out; 
} 
 
.gallery__image:hover { 
  transform: scale(1.03); 
} 
 
/* Lightbox */ 
.lightbox { 
  position: fixed; 
  top: 0; 
  left: 0; 
  width: 100vw; 
  height: 100vh; 
  opacity: 0; 
  pointer-events: none; 
  transition: opacity 200ms linear; 
} 
 
.lightbox.is-open { 
  opacity: 1; 
  pointer-events: initial; 
} 
 
.lightbox__overlay { 
  position: absolute; 
  top: 0; 
  left: 0; 
  width: 100vw; 
  height: 100vh; 
  background-color: rgba(0, 0, 0, 0.8); 
} 
 
.lightbox__content { 
  position: absolute; 
  top: 0; 
  left: 0; 
  height: 100vh; 
  width: 100vw; 
  display: flex; 
  align-items: center; 
  justify-content: center; 
  transform: scale(0.9); 
  transition: transform 200ms ease-in-out; 
} 
 
.lightbox___image { 
  width: auto; 
  height: auto; 
  max-height: 100vh; 
  max-width: 100vw; 
} 
 
.lightbox.is-open .lightbox__content { 
  transform: scale(1); 
} 
 
.lightbox__button { 
  position: absolute; 
  top: 8px; 
  right: 8px; 
  display: flex; 
  align-items: center; 
  justify-content: center; 
  width: 48px; 
  height: 48px; 
  padding: 0; 
  margin: 0; 
  border: none; 
  border-radius: 50%; 
  background-color: transparent; 
  color: #fff; 
  cursor: pointer; 
  transition: background-color 200ms ease-in-out; 
  outline: none; 
} 
 
.lightbox__button:hover, 
.lightbox__button:focus { 
  background-color: rgba(0, 0, 0, 0.5); 
} 
 
.lightbox__button i { 
  font-size: 36px; 
}
    <ul class="gallery"></ul> 
 
 
    <div class="lightbox is-open"> 
      <div class="lightbox__overlay"></div> 
 
      <div class="lightbox__content"> 
        <img class="lightbox___image " src="" alt="" /> 
      </div> 
 
      <button 
        type="button" 
        class="lightbox__button" 
        data-action="close-lightbox" 
      > 
        <i class="material-icons">close</i> 
      </button> 
    </div>

Answer 1

const images = [ 
  { 
    preview: 
      'https://cdn.pixabay.com/photo/2019/05/14/16/43/hokkaido-4202825__340.jpg', 
    original: 
      'https://cdn.pixabay.com/photo/2019/05/14/16/43/hokkaido-4202825_1280.jpg', 
    description: 'Hokkaido Flower', 
  }, 
  { 
    preview: 
      'https://cdn.pixabay.com/photo/2019/05/14/22/05/container-4203677__340.jpg', 
    original: 
      'https://cdn.pixabay.com/photo/2019/05/14/22/05/container-4203677_1280.jpg', 
    description: 'Container Haulage Freight', 
  }, 
  { 
    preview: 
      'https://cdn.pixabay.com/photo/2019/05/16/09/47/view-4206785__340.jpg', 
    original: 
      'https://cdn.pixabay.com/photo/2019/05/16/09/47/view-4206785_1280.jpg', 
    description: 'Aerial Beach View', 
  },]; 
const refs = { 
  galleryList: document.querySelector('ul.gallery'), 
  lightbox: document.querySelector('.lightbox'), 
  btn: document.querySelector('[data-action="close-lightbox"]') 
}; 
 
 
    
 
const createImage = (item, parent) => { 
  const { preview, original, description } = item; 
  const img = document.createElement('img'); 
   
  img.classList.add('gallery__image'); 
  img.dataset.source = original; 
  img.src = preview; 
  img.alt = description; 
   
  parent.appendChild(img); 
}; 
 
const createLink = (item, parent) => { 
  const { original } = item; 
  const a = document.createElement('a'); 
   
  a.classList.add('gallery__link'); 
  a.href = original; 
   
  createImage(item, a); 
   
  parent.appendChild(a); 
}; 
 
const createItem = (item) => { 
  const li = document.createElement('li'); 
  li.classList.add('gallery__item'); 
   
  createLink(item, li); 
   
  return li; 
}; 
 
const renderListItems = (arr) => { 
  const items = arr.map( (item) => createItem(item)); 
   
  refs.galleryList.append(...items); 
}; 
 
renderListItems(images); 
 
 
 
 
function onClickHandler(e) { 
  e.preventDefault(); 
   
  if(e.target.nodeName === 'IMG') { 
    refs.lightbox.classList.add('is-open'); 
    refs.lightbox.querySelector('.lightbox__image').src = e.target.src; 
    refs.lightbox.querySelector('.lightbox__image').alt = e.target.alt; 
  } 
} 
 
function onCloseHandler(e) { 
  if(e.target.nodeName === "I" || e.target.nodeName === "BUTTON") { 
    refs.lightbox.classList.remove('is-open'); 
  } 
} 
 
refs.galleryList.addEventListener('click', onClickHandler); 
refs.btn.addEventListener('click', onCloseHandler);
html { 
  box-sizing: border-box; 
} 
 
*, 
*::before, 
*::after { 
  box-sizing: inherit; 
} 
 
body { 
  margin: 0; 
  font-family: sans-serif; 
  background-color: #fff; 
  color: #212121; 
} 
 
.gallery { 
  display: grid; 
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); 
  grid-gap: 16px; 
  max-width: 1440px; 
  padding: 0; 
  margin: 0; 
  list-style: none; 
  margin-left: auto; 
  margin-right: auto; 
} 
 
.gallery__item { 
  position: relative; 
  box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2), 
    0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 2px 1px -1px rgba(0, 0, 0, 0.12); 
} 
 
.gallery__image { 
  display: block; 
  height: 100%; 
  width: 100%; 
  object-fit: cover; 
} 
 
.gallery__link { 
  display: block; 
  text-decoration: none; 
  height: 100%; 
} 
 
.gallery__icon { 
  position: absolute; 
  top: 50%; 
  left: 50%; 
  transform: translate(-50%, -50%); 
  display: flex; 
  align-items: center; 
  justify-content: center; 
  width: 56px; 
  height: 56px; 
  color: #fff; 
  cursor: pointer; 
  transition: opacity 200ms linear; 
  opacity: 0; 
  pointer-events: none; 
} 
.gallery__icon i { 
  font-size: 48px; 
} 
 
.gallery__item:hover .gallery__icon { 
  opacity: 1; 
} 
 
.gallery__image { 
  transition: transform 200ms ease-in-out; 
} 
 
.gallery__image:hover { 
  transform: scale(1.03); 
} 
 
/* Lightbox */ 
.lightbox { 
  position: fixed; 
  top: 0; 
  left: 0; 
  width: 100vw; 
  height: 100vh; 
  opacity: 0; 
  pointer-events: none; 
  transition: opacity 200ms linear; 
} 
 
.lightbox.is-open { 
  opacity: 1; 
  pointer-events: initial; 
} 
 
.lightbox__overlay { 
  position: absolute; 
  top: 0; 
  left: 0; 
  width: 100vw; 
  height: 100vh; 
  background-color: rgba(0, 0, 0, 0.8); 
} 
 
.lightbox__content { 
  position: absolute; 
  top: 0; 
  left: 0; 
  height: 100vh; 
  width: 100vw; 
  display: flex; 
  align-items: center; 
  justify-content: center; 
  transform: scale(0.9); 
  transition: transform 200ms ease-in-out; 
} 
 
.lightbox___image { 
  width: auto; 
  height: auto; 
  max-height: 100vh; 
  max-width: 100vw; 
} 
 
.lightbox.is-open .lightbox__content { 
  transform: scale(1); 
} 
 
.lightbox__button { 
  position: absolute; 
  top: 8px; 
  right: 8px; 
  display: flex; 
  align-items: center; 
  justify-content: center; 
  width: 48px; 
  height: 48px; 
  padding: 0; 
  margin: 0; 
  border: none; 
  border-radius: 50%; 
  background-color: transparent; 
  color: #fff; 
  cursor: pointer; 
  transition: background-color 200ms ease-in-out; 
  outline: none; 
} 
 
.lightbox__button:hover, 
.lightbox__button:focus { 
  background-color: rgba(0, 0, 0, 0.5); 
} 
 
.lightbox__button i { 
  font-size: 36px; 
}
<ul class="gallery"></ul> 
 
    <!-- 
      Модальное окно для полноразмерного изображения. 
      Для того чтобы открыть, добавь на div.lightbox CSS-класс is-open 
    --> 
    <div class="lightbox"> 
      <div class="lightbox__overlay"></div> 
 
      <div class="lightbox__content"> 
        <img class="lightbox___image " src="" alt="" /> 
      </div> 
 
      <button 
        type="button" 
        class="lightbox__button" 
        data-action="close-lightbox" 
      > 
        <i class="material-icons">close</i> 
      </button> 
    </div>

READ ALSO
Ошибка в Google Chrome, связанная с websocket

Ошибка в Google Chrome, связанная с websocket

Такие дела: есть сервер, к которому клиент подключается по адресу

250
Передача данных из js в php

Передача данных из js в php

Пишу калькулятор вкладовРазмер суммы устанавливается бегунком и выводится в поле c id="summa2"

371
Как в реакте изменить элемент массива в state

Как в реакте изменить элемент массива в state

Пытаюсь изменить элемент массива по методу handleToggle, но возникает ошибка, что я делаю не так, и почему в реакте я все время должен возвращать...

345
Error: TypeScript emitted no output for /frontend/src/index.tsx

Error: TypeScript emitted no output for /frontend/src/index.tsx

При компиляции webpack получаю такую ошибку:

474