Как показать изображение при наведении на ссылку: CSS hover, focus и мобильные устройства

Изображение при наведении на ссылку

Эффект, при котором рядом со ссылкой появляется изображение, можно реализовать без JavaScript — через CSS-позиционирование и состояния :hover и :focus. Такой приём подходит для необязательного превью: например, миниатюры товара, иллюстрации термина или дополнительной подсказки.

Но у старой схемы «показывать только при наведении мышью» есть важный недостаток: на сенсорных устройствах полноценного hover может не быть, а пользователь клавиатуры вообще не наводит курсор. Поэтому современная реализация должна учитывать несколько способов взаимодействия и не прятать критически важную информацию только в hover-эффекте.

Базовая разметка превью

Удобнее всего связать текст ссылки и изображение одним контейнером. Изображение изначально скрыто визуально, а затем показывается при взаимодействии.

Наглядный интерактивный пример. Наведите курсор на ссылку ниже. Изображение появится непосредственно под ней. При навигации с клавиатуры тот же эффект срабатывает после получения ссылкой фокуса.

Это рабочая демонстрация того же принципа, который разбирается далее. Для реального проекта CSS лучше вынести в stylesheet шаблона, а не хранить в статье.

<span class="preview-link">
  <a href="/example">Посмотреть пример</a>
  <img src="/images/example.webp"
       alt="Пример результата"
       class="preview-link__image">
</span>

Если картинка является лишь дополнительным превью содержимого ссылки, её alt должен быть кратким и не дублировать длинный текст без необходимости. Если изображение чисто декоративное, допустим пустой alt="".

Показ изображения через hover и focus

Сам эффект можно построить на opacity и visibility. В отличие от постоянного переключения display: none, такой подход позволяет добавить плавный переход.

.preview-link {
  position: relative;
  display: inline-block;
}

.preview-link__image {
  position: absolute;
  z-index: 10;
  left: 0;
  top: calc(100% + .5rem);
  width: 240px;
  max-width: 70vw;
  height: auto;

  opacity: 0;
  visibility: hidden;
  transform: translateY(.25rem);
  transition: opacity .18s ease, transform .18s ease;
}

.preview-link:hover .preview-link__image,
.preview-link:focus-within .preview-link__image {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

:focus-within срабатывает, когда фокус находится на вложенной ссылке. Благодаря этому превью доступно не только мыши, но и при переходе по странице клавишей Tab.

Как не дать картинке выйти за экран

Абсолютно позиционированное превью может обрезаться у правого края контейнера или экрана. Простое max-width: 70vw ограничивает размер, но не решает все варианты позиционирования.

Для обычной статьи можно выбрать направление открытия заранее, например показывать изображение справа или слева от ссылки. Если блок должен умно перестраиваться около каждого края окна, это уже задача popover/tooltip-компонента с JavaScript и вычислением позиции.

Также проверьте, не имеет ли родительский контейнер overflow: hidden: в таком случае всплывающее изображение может быть обрезано независимо от высокого z-index.

Что делать на мобильных устройствах

Многие сенсорные устройства не имеют удобного постоянного состояния hover. CSS media feature hover позволяет определить, способен ли основной механизм ввода нормально наводиться на элементы.

@media (hover: hover) {
  .preview-link:hover .preview-link__image {
    opacity: 1;
    visibility: visible;
  }
}

На мобильной версии лучше не делать важный контент зависимым от наведения. Если превью действительно нужно пользователю, практичнее показать миниатюру постоянно, открыть её по нажатию отдельной кнопкой или использовать компонент, рассчитанный на touch-ввод.

Почему одного hover недостаточно.

Эффект наведения — дополнительный визуальный канал, а не универсальный способ взаимодействия. Если изображение содержит информацию, без которой нельзя понять ссылку или выполнить действие, эту информацию нужно продублировать доступным способом.

При проектировании такого блока полезно проверить несколько сценариев:

  • пользователь работает мышью и видит :hover;
  • пользователь переходит по ссылкам клавишей Tab;
  • страница открыта на смартфоне или планшете;
  • изображение не загрузилось;
  • пользователь увеличил масштаб страницы;
  • контейнер находится около края экрана.

Если хотя бы в одном сценарии пропадает ключевой смысл, hover-превью нужно считать декоративным дополнением и перестроить основной интерфейс.

Всплывающий текст вместо изображения.

Для короткой поясняющей подписи можно использовать сходную механику. Однако не стоит имитировать системный tooltip через огромный скрытый блок, если задача решается обычным текстом рядом с элементом.

<span class="hint-link">
  <a href="/term">Термин</a>
  <span class="hint-link__text">Короткое пояснение</span>
</span>

Если подсказка интерактивная, содержит кнопки или должна управляться Escape/кликом, лучше использовать полноценный popover-компонент с корректной логикой фокуса, а не только CSS.

Вариант с transform вместо резкого появления.

Небольшая анимация может сделать появление мягче, но не должна задерживать доступ к содержимому. Для эффекта достаточно изменить прозрачность и смещение на несколько пикселей.

.preview-link__image {
  opacity: 0;
  transform: translateY(4px);
}

.preview-link:hover .preview-link__image,
.preview-link:focus-within .preview-link__image {
  opacity: 1;
  transform: translateY(0);
}

Анимировать большие перемещения или долгие задержки не стоит: пользователь должен сразу понимать, что произошло после наведения или фокуса.

Когда лучше использовать JavaScript

CSS подходит для простого необязательного превью. JavaScript оправдан, если нужно открывать блок по нажатию, закрывать по Escape, вычислять позицию относительно viewport, загружать изображение по требованию, управлять несколькими состояниями или обеспечить полноценное поведение popover.

Не нужно подключать библиотеку только ради одной картинки на hover, но и пытаться реализовать сложный интерактивный компонент исключительно CSS тоже не всегда разумно. Выбор зависит от требований к поведению и доступности.

Пример: превью изображения сбоку от ссылки.

В длинном справочнике иногда удобнее показывать картинку не под ссылкой, а рядом с ней. Для этого меняется только позиционирование, а принцип остаётся тем же:

.preview-link__image--side {
  top: 50%;
  left: calc(100% + 1rem);
  transform: translateY(-50%);
}

.preview-link:hover .preview-link__image--side,
.preview-link:focus-within .preview-link__image--side {
  opacity: 1;
  visibility: visible;
}

Такой вариант подходит только при достаточном свободном месте. В узкой колонке изображение может выйти за viewport, поэтому для адаптивной версии стоит возвращать его вниз или вообще отключать всплывающее позиционирование.

Не загружайте огромный оригинал ради маленького превью.

Если пользователь увидит картинку шириной 240–320 пикселей, нет смысла каждый раз передавать исходник на несколько мегабайт. Для превью лучше подготовить оптимизированную версию WebP/AVIF подходящего размера, указать реальные width и height либо сохранить корректное соотношение сторон. Это уменьшает объём загрузки и снижает риск скачков разметки.

Для изображения, которое изначально скрыто и может вообще не понадобиться пользователю, можно рассмотреть loading="lazy". Однако фактическое поведение загрузки зависит от браузера и положения элемента, поэтому оптимизация размера файла всё равно остаётся важнее надежды на lazy loading.

Что делать, если превью перекрывает соседние элементы.

У z-index есть смысл только внутри правил наложения и stacking context. Если родительский элемент создаёт собственный контекст или обрезает содержимое через overflow: hidden, увеличение z-index до огромного числа может ничего не изменить. Сначала нужно проверить структуру контейнеров в DevTools, а уже затем менять уровни наложения.

В карточках и таблицах часто безопаснее размещать превью внутри общего контейнера без обрезания либо использовать отдельный popover-слой, который добавляется ближе к корню документа.

Превью не должно мешать переходу по самой ссылке.

Если большая картинка появляется прямо под курсором и перекрывает соседние ссылки, пользователь может случайно потерять hover-состояние или нажать не туда. Старайтесь оставлять небольшой зазор и продумывать область появления. Для чисто информационной картинки можно использовать pointer-events: none, чтобы она не перехватывала мышь:

.preview-link__image {
  pointer-events: none;
}

Но это решение не подходит, если внутри всплывающего блока есть кнопки, ссылки или другие интерактивные элементы. В таком случае нужен полноценный компонент с управлением фокусом и событиями.

Итог

Для простого изображения при наведении достаточно контейнера с position: relative, абсолютно позиционированной картинки и состояний :hover + :focus-within. При этом важно помнить о touch-устройствах, границах viewport и клавиатурной навигации.

Эта статья теперь входит в объединённый раздел HTML/CSS, потому что сам эффект строится на совместной работе HTML-разметки и CSS-состояний.

Комментарии  
+1
Супер.Спасибо.
Добавить комментарий

Нашли ошибку или неточность?

Выделите фрагмент текста и нажмите Ctrl + Enter. Также можно воспользоваться кнопкой ниже.

Нашли ошибку или неточность?

Выделенный фрагмент будет добавлен в сообщение автоматически. При необходимости добавьте пояснение.

Форма защищена от автоматических отправок

Данные используются только для проверки и исправления материала.