Просмотр изображений в модальном окне сайта
Создание модального окна для изображений с помощью CSS и JavaScript превращает обычную миниатюру в удобный просмотрщик полноразмерного файла. Пользователь нажимает на изображение, фон страницы затемняется, а сам файл открывается поверх контента без перезагрузки. Такой сценарий подходит для фотографий, скриншотов, схем и демонстраций, которым нужно детальное рассмотрение.
В примере используется нативный элемент <dialog>: он управляет фокусом, блокирует взаимодействие с фоном и закрывается клавишей Escape. Разметка строится вокруг ссылок на изображения, поэтому при отключённом JavaScript они сохраняют обычное открытие файла. CSS отвечает за затемнение, размеры и расположение элементов, а JavaScript переключает снимки и запускает окно.
Подход и возможности окна
Для обычной галереи практично выбирать между нативным элементом <dialog>, собственной реализацией на <div> и готовой библиотекой. Критически важны не только внешний вид, но и управление фокусом, закрытие с клавиатуры, блокировка прокрутки страницы и корректная работа на сенсорных устройствах.
| Подход | Преимущества | Ограничения | Практический выбор |
|---|---|---|---|
<dialog> |
Встроенная модальность, блокировка фокуса, закрытие Escape | Нужно самостоятельно оформить внешний вид | Обычная галерея на сайте |
<div> |
Полный контроль над разметкой и анимацией | Фокус, Escape и прокрутку приходится реализовывать самостоятельно | Нестандартный интерфейс |
| Готовая библиотека | Быстрое подключение и дополнительные возможности | Появляется внешняя зависимость | Прототип или временное решение |
Нативный <dialog> подходит для большинства задач. Элемент создаёт отдельный слой поверх страницы, возвращает управление клавиатурой выбранному окну и восстанавливает прежний фокус после закрытия. Стили по умолчанию можно полностью переопределить, сохранив встроенное поведение.
Перед публикацией стоит проверить результат в нескольких браузерах и на разных версиях операционных систем. Заметки о расширениях, настройках и особенностях веб-страниц можно найти на ресурсе AyudаGPlus. Особое внимание следует уделить мобильным браузерам, где жесты и изменение ширины окна могут по-разному влиять на расположение изображения.
Разметка и оформление галереи
Каждая миниатюра оформляется как обычная ссылка. Атрибут href указывает на полноразмерную версию, а класс preview связывает её с обработчиком JavaScript. Такой подход сохраняет возможность открыть изображение даже тогда, когда скрипты недоступны.
<a class="preview" href="/images/photo-full.jpg">
<img src="/images/photo-small.jpg"
alt="Вид на горное озеро утром"
width="640"
height="420">
</a>
<dialog id="image-dialog" class="image-dialog"
aria-labelledby="image-caption">
<button class="dialog-close" type="button"
aria-label="Закрыть окно">
Закрыть
</button>
<button class="dialog-prev" type="button">
Назад
</button>
<figure>
<img id="dialog-image" src="" alt="">
<figcaption id="image-caption"></figcaption>
</figure>
<button class="dialog-next" type="button">
Далее
</button>
</dialog>
Базовые стили задают тёмный фон, ограничивают ширину окна и оставляют достаточное расстояние от краёв экрана. Свойство object-fit: contain сохраняет пропорции изображения, не обрезая важные части фотографии или схемы.
.preview {
display: block;
cursor: zoom-in;
}
.preview img {
display: block;
width: 100%;
height: auto;
}
.image-dialog {
width: min(92vw, 1100px);
max-width: none;
max-height: 92vh;
padding: 1rem;
overflow: auto;
border: 0;
background: #111318;
color: #ffffff;
}
.image-dialog::backdrop {
background: rgb(0 0 0 / 78%);
}
.image-dialog[open] {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 1rem;
align-items: center;
}
.dialog-close {
grid-column: 1 / -1;
justify-self: end;
}
.image-dialog figure {
grid-column: 2;
min-width: 0;
margin: 0;
}
#dialog-image {
display: block;
max-width: 100%;
max-height: 72vh;
margin: 0 auto;
object-fit: contain;
}
#image-caption {
margin-top: 0.75rem;
text-align: center;
}
.dialog-prev,
.dialog-next,
.dialog-close {
padding: 0.6rem 0.9rem;
border: 1px solid #656b75;
border-radius: 0.4rem;
background: #242832;
color: #ffffff;
cursor: pointer;
}
.dialog-prev {
grid-column: 1;
grid-row: 2;
}
.dialog-next {
grid-column: 3;
grid-row: 2;
}
body.dialog-open {
overflow: hidden;
}
:focus-visible {
outline: 3px solid #7dd3fc;
outline-offset: 3px;
}
Подпись внутри окна можно получать из атрибута alt миниатюры. Если у изображения есть содержательное описание, пользователь увидит его при открытии и не потеряет контекст после увеличения. Размеры width и height у миниатюры помогают браузеру зарезервировать место и уменьшить смещение страницы при загрузке.
Открытие, навигация и закрытие
JavaScript собирает все ссылки с классом preview, сохраняет текущий индекс и меняет источник большого изображения. Вызов showModal() переводит элемент в модальный режим, а класс для страницы предотвращает прокрутку фона.
const dialog = document.querySelector("#image-dialog");
const dialogImage = dialog.querySelector("#dialog-image");
const caption = dialog.querySelector("#image-caption");
const links = Array.from(document.querySelectorAll(".preview"));
let currentIndex = 0;
function showImage(index) {
const activeLink = links[index];
const thumbnail = activeLink.querySelector("img");
currentIndex = (index + links.length) % links.length;
dialogImage.src = activeLink.href;
dialogImage.alt = thumbnail.alt;
caption.textContent = thumbnail.alt;
}
function openDialog(index) {
showImage(index);
if (!dialog.open) {
dialog.showModal();
}
document.body.classList.add("dialog-open");
}
function closeDialog() {
dialog.close();
}
links.forEach((link, index) => {
link.addEventListener("click", (event) => {
event.preventDefault();
openDialog(index);
});
});
dialog.querySelector(".dialog-close")
.addEventListener("click", closeDialog);
dialog.querySelector(".dialog-prev")
.addEventListener("click", () => {
showImage(currentIndex - 1);
});
dialog.querySelector(".dialog-next")
.addEventListener("click", () => {
showImage(currentIndex + 1);
});
Закрытие по Escape уже работает средствами <dialog>, поэтому дублировать эту клавишу не требуется. Для закрытия щелчком по затемнённому фону можно проверить, находится ли точка нажатия за границами окна. Проверка по координатам предотвращает случайное закрытие при нажатии на свободное место внутри диалога.
dialog.addEventListener("click", (event) => {
if (event.target !== dialog) {
return;
}
const bounds = dialog.getBoundingClientRect();
const outside =
event.clientX < bounds.left ||
event.clientX > bounds.right ||
event.clientY < bounds.top ||
event.clientY > bounds.bottom;
if (outside) {
closeDialog();
}
});
dialog.addEventListener("close", () => {
document.body.classList.remove("dialog-open");
});
dialog.addEventListener("keydown", (event) => {
if (event.key === "ArrowLeft") {
showImage(currentIndex - 1);
}
if (event.key === "ArrowRight") {
showImage(currentIndex + 1);
}
});
Кнопки «Назад» и «Далее» дополняются стрелками клавиатуры. Циклический переход удобен, когда в галерее много изображений: после последнего снимка открывается первый, а перед первым — последний. Обработчик закрытия снимает блокировку прокрутки, даже если окно закрыли клавишей Escape.
Мобильный экран, доступность и безопасность
На узких экранах кнопки навигации лучше перенести под изображение. Медиазапрос меняет сетку на одну колонку, увеличивает область нажатия и ограничивает высоту снимка отдельно от окна. Так изображение остаётся видимым, а подпись и управляющие элементы не выходят за пределы экрана.
@media (max-width: 640px) {
.image-dialog {
width: 96vw;
max-height: 94vh;
padding: 0.75rem;
}
.image-dialog[open] {
grid-template-columns: 1fr;
}
.dialog-close,
.dialog-prev,
.image-dialog figure,
.dialog-next {
grid-column: 1;
}
.dialog-prev {
grid-row: 2;
}
.image-dialog figure {
grid-row: 3;
}
.dialog-next {
grid-row: 4;
}
#dialog-image {
max-height: 62vh;
}
}
Доступное окно требует понятных подписей у кнопок, корректного альтернативного текста и заметного состояния фокуса. Нативный <dialog> удерживает клавиатурный фокус внутри окна, но разработчику всё равно нужно проверить порядок перехода между элементами. Декоративные изображения должны иметь пустой alt, а содержательные — точное описание без пересказа назначения кнопки.
Данные для подписи и источника файла не стоит собирать из недоверенного HTML. Присваивание через textContent и свойство src безопаснее вставки через innerHTML. Если адреса или описания поступают от пользователей, их необходимо проверять. Дополнительные соображения о безопасной работе с интернетом можно найти в материалах об интернете.
Добавьте модальный просмотрщик на сайт
В теме WordPress разметку галереи удобно выводить внутри цикла, используя одинаковый класс preview для всех миниатюр. Полный адрес можно передавать через href, а скрипт подхватит его автоматически. Это позволяет менять исходные файлы и порядок снимков без изменения обработчика.
После подключения проверьте открытие и закрытие мышью, клавишей Escape и кнопками навигации. Отдельно протестируйте длинную подпись, широкую фотографию, вертикальный снимок и несколько галерей на одной странице. На узком экране убедитесь, что изображение не выходит за границы окна, а прокрутка страницы возобновляется после закрытия.
Внедрите готовый обработчик в файлы темы, проверьте результат на телефоне и компьютере и сохраните модальное окно как удобный способ просмотра публикуемых изображений.