image Доброго времени суток, дорогие хабрахабровцы!

При создании адаптивной версии сайта, часто бывают ситуации, когда надо знать: поддерживает ли браузер пользователя hover, или нет. К примеру, выпадающее при наведении подменю, или же различные анимации, привязанные к событию hover — все это нужно только на ПК. На touch-устройствах поведение элементов должно меняться. Так как же задать определенные стили только для устройств с поддержкой hover на css, не забывая о кроссбраузерности?

В начале сразу хочу сказать о том, что есть js-библиотека Modernizr, которая решает эту задачу. Но… Прикручивать эту библиотеку только для определения поддержки hover, вместо написания одного медиа-запроса в css — не лучший вариант, на мой взгляд.

Но и с медиа-запросами не все так просто. Давайте посмотрим, какие есть варианты!

1. media (hover)

div{color:red;}
@media (hover){
/*Поддерживает hover*/
div{color:green;}
}

Данный медиа-запрос позволит отдельно прописывать стили только для устройств, с поддержкой hover. Но давайте посмотрим на поддержку браузерами: caniuse.com/#feat=css-media-interaction

Как видим, он не поддерживается IE и Firefox. Т.е. наш код будет воспринимать эти браузеры, как без поддержки hover. Такой вариант нам не подходит!

2. media (pointer:coarse)

div{color:green;}
@media (pointer:coarse){
/*touch-устройство*/
div{color:red;}
}

Этот запрос работает в точности наоборот, т.е. только для touch-устройств. Поддержка такая же, как и у media (hover), но т.к. IE и Firefox являются браузерами для ПК — то в них и так не должен срабатывать данный медиа-запрос.

Такое решение вполне подходит для тех случаев, когда нужно прописать стили именно для touch-устройств. Но в основном, стоит задача — прописать стили именно для hover. А это означает, что придется сначала писать стили для всех браузеров, а потом сбрасывать их в медиа-запросе. Это не только увеличивает код, но и довольно неудобно, т.к. надо дублировать каждое свойство, измененное при событии hover на элементе.

3. media (hover) +

Выбирая из двух вышеупомянутых вариантов, первый наиболее привлекательный. Была бы еще браузерная поддержка побольше…

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

Итак, данный код будет работать только в IE:

@media (min-width:0\0) {}

А этот медиа-запрос сработает только для Firefox:
@media (min--moz-device-pixel-ratio:0) {}

Так давайте же объединим все 3 запроса!
div{color:red;}
@media (hover) , (min-width:0\0) , (min--moz-device-pixel-ratio:0){
/*Поддерживает hover*/
div{color:green;}
}

В результате получаем универсальный медиа-запрос, срабатывающий при поддержке hover.
Запрос поддерживается во всех основных браузерах, и как бонус, корректно работает на Blackberry и в Opera Mini (чего не было во 2 варианте).

Демо сравнения 3х подходов. Зеленый цвет = поддержка hover:

Комментарии (30)


  1. k12th
    07.02.2018 15:18

    @media (min-width:0\0) {}
    Прям как на десять лет назад перенёсся! Грязноватый, конечно, хак, но весьма полезный.


    А как это сделать в JS, не грепая navigator.userAgent?


    1. noodles
      07.02.2018 15:31
      +1

      function isTouchDevice() {
        try {
          document.createEvent('TouchEvent');
          return true;
        }
        catch(e) {
          return false;
        }
      }
      


      1. k12th
        07.02.2018 15:33

        Занятно, спасибо.


  1. ad1Dima
    07.02.2018 15:33
    +3

    IE и Firefox являются браузерами для ПК — то в них и так не должен срабатывать данный медиа-запрос.

    Откройте уже для себя 2-в-1 девайсы.


    1. EvilFox
      07.02.2018 15:44

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


      1. SelenIT3
        07.02.2018 16:21

        Формально в природе существуют мобильный IE (на старых виндофонах) и мобильный Огнехвост (в принципе, везде, кроме iOS), и как я понимаю, для них предложенное решение даст «ложноположительный» результат. Конечно, их так мало, что этим можно пренебречь, но всё же..:)


        1. ad1Dima
          07.02.2018 16:28

          Сейчас кстати проверил на W10 mobile: если мышь не подключать — все квадраты красные. Но если подключить, то средний становится зелёным.


        1. EvilFox
          07.02.2018 16:33

          Сенсорное управление работает и в настольных версиях.


          1. SelenIT3
            07.02.2018 16:37

            Насколько я понимаю, не дать :hover тем, кому он технически доступен — меньшее зло, чем наоборот, заставить полагаться на него тех, у кого его в принципе нет. Конечно, задачи разные бывают, но в общем случае, наверное, важнее отловить отсутствие мышки, чем наличие тача.


            1. EvilFox
              07.02.2018 17:05

              Ну выше речь была только про вариант pointer:coarse от которого автор отказался в пользу варианта hover+костыли.
              Вариант с hover+костыли более правильный, устройства 2-в-1 на любых обозревателях будут работать нормально.
              А вот pointer:coarse сломает сенсорную часть на IE и Firefox:
              caniuse.com/#feat=css-media-interaction
              Эти правила просто не выполнятся.


              1. ad1Dima
                07.02.2018 17:44

                Нормально, это в hover режиме?


                1. EvilFox
                  07.02.2018 17:48

                  Там же у него не только hover, но и костыли для тех у кого нет его поддержки.

                  Конечно ещё есть мобильный ие и мобильная лиса, они будут обрабатываться как 2-в-1 даже если у них нет hover.


                  1. ad1Dima
                    07.02.2018 19:13

                    Кажется, вы не осознаете проблему.
                    Вот я лежу на диване со своим виндовым планшетом (Surface, Yoga, etc.) и серфлю инет с помощью тача, потому что так удобнее. Сайт автора определит, что у меня работает ховер, и я со своим тачскрином обламаюсь в нормальном использовании сайта.

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


                    1. EvilFox
                      07.02.2018 20:14

                      Сайт автора определит, что у меня работает ховер, и я со своим тачскрином обламаюсь в нормальном использовании сайта.
                      Сильно зависит от того как стили используются. Я хотел про это написать да вылетело из головы.
                      Если стили hover перекрывают стили под тач и ломают сенсорную работу то это само собой не нормально.

                      Моя мысль в том, что hover должен быть не основным инструментом, а дополнительным
                      Если уйти за рамки статьи то тут двоякая ситуация. Двоякость в том что интерфейс можно строить двумя способами:
                      1. заточенный сугубо под тот или иной вид взаимодействия (довольно сильно отличается от другого). Тут hover в одном случае будет как основной инструмент, в другом он вовсе будет отсутствовать. Но на деле различий будет сильно больше (те же разные размеры кнопок, наличие или отсутствие свайпов и т. д.)
                      2. компромиссный, где в среднем угождают всем, мудрят с адаптивностью и есть вспомогательные элементы разметки для focus событий (пример ссылки с всплывающей подсказкой, где под сенсорные устройства есть отдельный значок-кнопка т.к. по ссылке происходит переход). Тут грубо говоря и hover и focus рядом соседствуют и всё как-то усреднено.
                      В первом случае приоритет даётся основному назначению устройства, но в случае с 2-в-1 это не всегда возможно определить. Поэтому обычно всегда есть возможность переключиться в другой режим.
                      Во втором никакого приоритета не существует всё и так работает всегда.

                      Можно ещё скрестить эти два подхода и сделать морфирующийся интерфейс (плавное переключение на лету от поведения пользователя), но он всё равно не до конца решит проблему — в начале пользователь видит определённый вид интерфейса и думает что раз нет больших кнопок то надо тянуться к мышке (заветного тач события не происходит). То есть всё равно придётся в начале предлагать ему переключение чтобы он понял что интерфейс это вообще умеет.


                      1. ad1Dima
                        07.02.2018 20:22

                        Если стили hover перекрывают стили под тач и ломают сенсорную работу то это само собой не нормально.
                        ну статья подразумевает, что или hover или тач.

                        Я не хочу больших кнопок. Даже мои не самые тонкие пальцы прекрасно попадают по всем кнопкам на десктопном хабре (кроме стрелочек у кнопки обновить комментарии, там сложно).

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


    1. lipton_ice_tea Автор
      07.02.2018 16:15

      На 2-в-1 девайсах будет активен hover. Функционал не потеряется


      1. ad1Dima
        07.02.2018 16:25

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


  1. Kiyoshi
    07.02.2018 16:16

    но т.к. IE и Firefox являются браузерами для ПК — то в них и так не должен срабатывать данный медиа-запрос.


    т.е. ноутбуки-перевертыши с тачем пролетают мимо?


    1. lipton_ice_tea Автор
      07.02.2018 16:16

      На 2-в-1 девайсах будет активен hover. Функционал не потеряется.


      1. EvilFox
        07.02.2018 16:37

        Только для сенсора нужен focus.


        1. EvilFox
          07.02.2018 16:49

          Ой, пардон, спросонья не так понял ситуацию.


  1. dmnmkua
    07.02.2018 16:16

    Прошу прощения, может я чего-то не понимаю, но у hover 100% поддержка (кроме IE6).


    1. lipton_ice_tea Автор
      07.02.2018 16:17

      см. ссылку на caniuse в тексте статьи =)


  1. ShadowOfCasper
    07.02.2018 16:18

    Я один не понял зачем через медиазапрос с так себе поддержкой выделять устройства на которых есть или нет наведения? На дворе 18й год и стилусы, курсоры на мобильных устройствах канули в небытие времён. Почему бы не упростить до:

    @media(max-width:1023px){
    Без ховеров
    }
    @media(min-width:1024px){
    Можно писать ховеры
    }
    

    В исключение только нетбуки попадут, но и это не так уж и критично. На тачах ховеры (описаны они кодом или нет) просто не проявятся (хотя вроде где-то проявляются как focus, но и это можно ресетнуть) — не вижу смысла усложнять.


    1. lipton_ice_tea Автор
      07.02.2018 16:22

      Т.к. вполне вероятно, что будет планшет с бОльшим разрешением экрана, а браузер на ноутбуке/пк можно свернуть в более узкое окно, и т.д. Много нюансов


    1. SelenIT3
      07.02.2018 16:27

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



  1. L2jLiga
    07.02.2018 16:22

    Но ведь данный media так же отработает и в Firefox mobile, как быть в таком случае?

    @media (min--moz-device-pixel-ratio:0) {}
    


    1. lipton_ice_tea Автор
      07.02.2018 16:28

      Не тестировал код в Firefox mobile. Но во первых, скорее всего, можно добавить еще один параметр в запрос для фильтра именно Firefox mobile. Во вторых надо смотреть, а есть ли пользователи сайта с таким браузером.


  1. SelenIT3
    07.02.2018 16:34

    Хочу предостеречь по поводу browserhacks.com — ресурс это очень старый, его создатель давно перестал уделять ему время, поэтому информация оттуда может быть весьма устаревшей и желательно ее перепроверять. Например, селектор :not(*:root) там преподносится как «хак для вебкитов», но это валидный селектор 4 уровня, и любой другой браузер запросто может в любой момент добавить его поддержку. Особенно в свете недавней активизации работы над селекторами в W3C.