Галерея — это две вещи: сетка маленьких картинок и окно,
в котором открывается большая. Обе делаются почти без кода:
сетку раскладывает CSS, а окно умеет сам браузер — для этого
есть тег <dialog>.
Когда галерея, а когда просто картинки
Если снимков три-четыре и каждый что-то объясняет, ставьте их прямо в текст: разглядывать их отдельно никто не будет. Галерея нужна, когда снимков десяток и больше, они равнозначны, а подробности видны только на большом размере, — фотографии, работы, скриншоты.
Рядом на этом сайте
Фоторяд шагов на странице про Open Server устроен ровно так же, как описано ниже: щелчок по снимку открывает его в окне. Разница только в раскладке — там шаги идут по порядку, а в галерее снимки равнозначны.
Разметка: сетка ссылок, а не сетка картинок
Главный приём здесь такой: превью оборачивается ссылкой на большой файл. Тогда галерея работает даже с выключенным JavaScript — щелчок просто откроет снимок отдельной страницей. Скрипт потом перехватит этот щелчок и покажет окно.
<ul class="gallery">
<li class="gallery__item">
<a class="gallery__link" href="/assets/img/work-1.webp" data-lightbox>
<img src="/assets/img/work-1-thumb.webp"
width="400" height="300" loading="lazy"
alt="Главная страница сайта школы: шапка и первый экран">
</a>
</li>
</ul>
Атрибуты width и height обязательны,
даже если размер задан в CSS. Браузер по ним заранее оставляет
место под картинку, и текст под галереей не прыгает, пока
снимки грузятся. А loading="lazy" откладывает
загрузку тех, до которых читатель ещё не долистал.
Окно увеличения: готовый тег вместо своего кода
Самодельное окно из <div> нужно учить
четырём вещам: закрываться по Esc, не выпускать фокус наружу,
возвращать фокус на ту картинку, с которой открылось,
и затемнять страницу. Тег <dialog>,
открытый методом showModal, делает всё это сам.
const box = document.createElement('dialog');
box.className = 'lightbox';
box.innerHTML = '<img class="lightbox__img" alt="">';
document.body.append(box);
document.addEventListener('click', (event) => {
const link = event.target.closest('a[data-lightbox]');
if (!link) return;
event.preventDefault(); // не уходим на страницу снимка
const img = box.querySelector('img');
img.src = link.href;
img.alt = link.querySelector('img').alt; // подпись берём у превью
box.showModal(); // Esc и возврат фокуса — сами
});
// щелчок по затемнению закрывает окно
box.addEventListener('click', (event) => {
if (event.target === box) box.close();
});
Обработчик висит на всём документе, а не на каждой ссылке:
так галерея переживёт добавление снимков скриптом и не
потребует переподписки. Затемнение настраивается
псевдоэлементом ::backdrop — это тоже
готовая возможность браузера.
Разметку окна пишет скрипт
В HTML страницы пустого <dialog> быть
не должно. Иначе на странице без JavaScript висит скрытый
блок, а на странице с ним — разметка, которую придётся
копировать во все файлы сайта. Окно нужно ровно тогда,
когда по снимку щёлкнули, — тогда его и создаём.
Вес страницы: превью — это отдельный файл
Частая ошибка новичка — поставить в сетку тот же файл, что открывается в окне, и уменьшить его стилями. Страница с двадцатью снимками по три мегабайта весит шестьдесят мегабайт и на телефоне не открывается вовсе.
| Файл | Размер | Формат |
|---|---|---|
| Превью в сетке | 400×300, до 60 КБ | WebP |
| Большой снимок в окне | по длинной стороне 1600, до 400 КБ | WebP |
Что попросить у нейросети
Нужен лайтбокс на чистом JavaScript, без библиотек
и сборщика. Разметка галереи: список, в каждом пункте
ссылка с атрибутом data-lightbox на большой
снимок, внутри ссылки превью с осмысленным
alt.
Окно сделай тегом dialog и открывай методом
showModal, разметку окна создавай скриптом,
в HTML её быть не должно. Нужны кнопки «Предыдущий»
и «Следующий», переход по стрелкам клавиатуры,
закрытие по Esc и по щелчку в затемнение, подпись
под снимком из alt превью.
Пока большой снимок грузится, покажи в окне превью, растянутое на его место, — иначе окно открывается пустым. Комментарии по-русски.
Про стрелки и Esc. Esc обрабатывать
не нужно: showModal уже закрывает окно этой
клавишей. А вот стрелки повесьте на событие
keydown самого окна, а не документа, —
иначе они будут листать галерею и тогда, когда окно
закрыто.
Про подпись. Если подпись выводится
текстом под снимком, у картинки в окне
alt надо оставить пустым. Иначе
программа чтения экрана прочитает одно и то же дважды.
Про прокрутку под окном. При
showModal браузер сам не всегда блокирует
прокрутку страницы позади. Надёжнее на время открытого
окна ставить overflow: hidden элементу
html и снимать при закрытии.
Про подмену снимка. Ставьте
src превью сразу, а большой снимок грузите
в отдельный объект Image и подставляйте
по событию загрузки. Тогда окно никогда не мигает белым.
Галерея из Evolution CMS
Снимки в CMS хранят двумя способами. Первый — TV-поле типа «изображение» с возможностью добавлять строки: вся галерея лежит в одной странице, и редактор добавляет снимки кнопкой «Добавить». Второй — папка ресурсов, где каждый снимок отдельная запись с описанием и датой.
| Способ | Когда подходит |
|---|---|
| TV-поле со списком картинок | Галерея принадлежит одной странице: снимки товара, фото мероприятия |
| Папка ресурсов | Снимки нужны и по отдельности: у каждого свой адрес, описание, дата |
В обоих случаях разметку одного снимка задаёт чанк — та же,
что в первом блоке кода, только с метками полей вместо
настоящих адресов. Превью при этом делает не редактор
вручную: уменьшенную копию строит phpThumb,
которому передают нужную ширину.
Частые ошибки
| Ошибка | Чем плохо | Как правильно |
|---|---|---|
| Превью — это большой файл в CSS | Страница весит десятки мегабайт | Отдельный уменьшенный файл под сетку |
Окно собрано из div |
Фокус уходит на страницу позади, Esc не закрывает | Тег dialog и метод showModal |
| Превью не обёрнуто ссылкой | Без JavaScript снимок не открыть никак | Ссылка на большой файл, щелчок перехватывает скрипт |
| У картинок нет размеров | Текст прыгает, пока снимки грузятся | width и height в разметке |
Пустой alt у всех снимков |
Для части читателей галереи просто нет | Описание того, что на снимке, у каждого превью |
Что должно получиться
- Щелчок по превью открывает снимок в окне поверх страницы
- Окно закрывается по Esc и щелчком в затемнение, фокус возвращается на превью
- С выключенным JavaScript снимок всё равно открывается по ссылке
- Сетка не прыгает при загрузке: у картинок заданы размеры
- Превью весит десятки килобайт, а не мегабайты