Изменяем поведение элементов при наличии точного указателя

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

Но на сенсорных экранах у нас нет такого точного указателя, как курсор, и даже если срабатывает событие наведения :hover, то эффект остаётся и после того, как мы уберем палец/стилус с элемента, и будет сохранятся до тех пор, пока не произойдёт другое событие. Это некорректное поведение и может запутать пользователя.

На данный момент, есть специальные @media запросы, которые определяют наличие точного указателя, и в статье мы рассмотрим один из вариантов их применения для решения вышеописаной проблемы



Прежде чем начать, хотел бы обратить внимание, что у блога есть телеграм канал https://t.me/frontips, где можно узнавать о выходе новых статей и со временем будет появляться больше интересной и полезной информации.

Поддержите развитие блога и канала подпиской!

А теперь перейдём к теме статьи ;) Приятного чтения!



Немного теории

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

Understanding Success Criterion 2.5.6: Concurrent Input Mechanisms

MDN Web Docs - pointer

MDN Web Docs - hover

MDN Web Docs - any-pointer

MDN Web Docs - any-hover


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

Устройства ввода могут быть точными указателями, как курсор мыши, и могут быть неточными, как касание сенсорного экрана пальцем/стилусом

Перейдём к @media запросам - итак, в нашем распоряжении четыре @media запроса - pointer, hover, any-pointer, any-hover

В чем их отличие?

@media запросы pointer и hover опираются только на основное(первичное) устройство ввода. Так как мы не хотим ограничивать пользователя единственным способом ввода, мы будем использовать другие @media запросы

@media запрос any-pointer будет выполнен, если хотя бы одно устройство ввода имеет точный указатель

@media запрос any-hover будет выполнены если хотя бы одно устройство ввода имеет возможность наведения, то есть может “повиснуть” над элементом, не производя над ним никаких других манипуляций

any-pointer имеет следующие значения

  • none - указатель отсутствует
  • coarse - есть хотя бы один указатель ограниченной точности
  • fine - есть хотя бы один точный указатель

any-hover имеет следующие значения

  • none - нет устройств ввода, которые имеют возможность наведения
  • hover - есть хотя бы одно устройство ввода, которое имеет возможность наведения


Применение на практике

Применять эту возможность на практике можно в случаях, когда нужно или изменить внешний вид элемента, или его поведение в зависимости от устройства, на котором просматривается сайт

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

Теперь рассмотрим пример, который был описан в начале статьи.

2

Структура примера

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Practice</title>
    <link rel="stylesheet" href="css/bootstrap-reboot.min.css"/>
    <link rel="stylesheet" href="css/main.css"/>
  </head>
  <body>
    <section class="section">
      <div class="container">
        <div class="section__grid grid">
          <div class="grid__items">
            <div class="grid__item"><img src="https://picsum.photos/256/256" alt=""/></div>
            <div class="grid__item"><img src="https://picsum.photos/256/257" alt=""/></div>
            <div class="grid__item"><img src="https://picsum.photos/256/258" alt=""/></div>
            <div class="grid__item"><img src="https://picsum.photos/256/259" alt=""/></div>
            <div class="grid__item"><img src="https://picsum.photos/256/260" alt=""/></div>
            <div class="grid__item"><img src="https://picsum.photos/256/261" alt=""/></div>
          </div>
        </div>
      </div>
    </section>
  </body>
</html>


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

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

.container {
  max-width: 1120px;
  padding: 0 16px;
  margin: 0 auto;
}
.section {
  padding: 64px 0;
  --color: #f7f3f3;
  background: var(--color);
  min-height: 100vh;
}
.grid {
  position: relative;
}
.grid__items {
  overflow-x: auto;
  display: flex;
  position: relative;
}
.grid__item {
  height: 128px;
  width: 128px;
  border-radius: 50%;
  background: var(--color);
  border: 2px solid #000;
  transition: 0.4s;
  overflow: hidden;
  position: relative;
  flex-shrink: 0;
}
.grid__item:not(:first-child) {
  margin-left: -1.4em;
}
.grid__item img {
  display: block;
  width: 100%;
  height: 100%;
  -o-object-fit: cover;
  object-fit: cover;
  transition: 0.4s;
  transform: scale(1.2);
}

1


Чтобы добавить элементам дополнительное поведение для устройств с точным указателем и с возможностью наведения на элемент, будем использовать комбинацию @media запросов

Добавим следующие стили

@media (any-pointer: fine) and (any-hover: hover) {
  .grid__item:not(:first-child) {
    margin-left: -4.4em;
  }
  .grid__item:hover img {
    transform: scale(1.2) translateX(-8%);
  }
  .grid__item:hover + * {
    margin-left: -1.4em;
  }
}

2

Посмотрим результат и разницу отображения и поведения на видео




Итоги

Если до этого мы в основном ориентировались только на разрешение устройства, и в зависимости от этого меняли внешний вид или поведение элементов, то теперь можно ориентироваться на возможности взаимодействия пользователя с устройством

Буду рад, если статья оказалась полезной

Спасибо за ваше внимание и уделённое время!



Друзья, стараюсь для вас, поддержите проект!

Подписывайтесь, впереди много всего интересного и полезного ;)

Telegram - https://t.me/frontips

VK - https://vk.com/frontips