Как предотвратить эффект липкого зависания для кнопок на сенсорных устройствах

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

  • Я мог бы добавить класс no-hover ontouchend для каждой кнопки и сделать мой CSS как это: button:not(.no-hover):hover { background-color: blue; }, но это, вероятно, довольно плохо для производительности, и не обрабатывать устройства, такие как Chromebook Pixel (который имеет как сенсорный экран и мышь).

  • Я мог бы добавить класс touch в documentElement и сделать мой CSS например: html:not(.touch) button:hover { background-color: blue; } Но это также не работает на устройствах с сенсорным и мыши.

Я бы предпочел удалить состояние зависания ontouchend. Но похоже, что это невозможно. Фокусировка другого элемента не снимает состояние зависания. Нажатие другого элемента выполняется вручную, но я не могу запустить его в JavaScript.

Все найденные решения кажутся несовершенными. Есть ли идеальное решение?

Ответ 1

Вы можете удалить состояние зависания, временно удалив ссылку из DOM. См. http://testbug.handcraft.com/ipad.html


В CSS вы имеете:

:hover {background:red;}

В JS у вас есть:

function fix()
{
    var el = this;
    var par = el.parentNode;
    var next = el.nextSibling;
    par.removeChild(el);
    setTimeout(function() {par.insertBefore(el, next);}, 0)
}

И затем в вашем HTML вы:

<a href="#" ontouchend="this.onclick=fix">test</a>

Ответ 2

Как только CSS Media Queries Level 4 реализована, вы сможете это сделать:

@media (hover: hover) {
    button:hover {
        background-color: blue;
    }
}

Или на английском языке: "Если браузер поддерживает правильное/истинное/реальное/неэмулируемое зависание (например, имеет основное устройство ввода-вывода), примените этот стиль, когда button зависнут."

Так как эта часть Media Queries Level 4 до сих пор была реализована только в Chrome Chrome, я написал polyfill для решения с этим. Используя его, вы можете преобразовать вышеупомянутый футуристический CSS в:

html.my-true-hover button:hover {
    background-color: blue;
}

(Вариант метода .no-touch). Затем, используя некоторый клиентский JavaScript из того же polyfill, который обнаруживает поддержку зависания, вы можете соответствующим образом переключить присутствие класса my-true-hover:

$(document).on('mq4hsChange', function (e) {
    $(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});

Ответ 3

Это обычная проблема без идеального решения. Поведение наведения полезно с помощью мыши и в основном вредно с прикосновением. Усугубляет проблему устройства, которые поддерживают touch и мышь (одновременно, не менее!), Такие как Chromebook Pixel и Surface.

Самое чистое решение, которое я нашел, это включить только поведение наведения, если устройство не считается поддерживающим сенсорный ввод.

var isTouch =  !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0;

if( !isTouch ){
    // add class which defines hover behavior
}

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

Я тестировал это на iPhone, iPad, Chromebook Pixel, Surface и различных устройствах Android. Я не могу гарантировать, что он будет работать, когда в микс добавляется общий USB-сенсорный ввод (например, стилус).

Ответ 4

С Modernizr вы можете ориентировать свои зависания специально для устройств без касания:

(Примечание: это не работает в системе фрагментов StackOverflow, вместо этого проверьте jsfiddle)

/* this one is sticky */
#regular:hover, #regular:active {
  opacity: 0.5;
}

/* this one isn't */
html.no-touch #no-touch:hover, #no-touch:active {
  opacity: 0.5;
}

Обратите внимание :active не обязательно указывать .no-touch так как он работает как на мобильном, так и на рабочем столе.

Ответ 5

Из 4 способа борьбы с липким зависанием на мобильном телефоне: здесь можно динамически добавлять или удалять класс can touch документ на основе текущего типа ввода пользователя. Он также работает с гибридными устройствами, где пользователь может переключаться между касанием и мышью/трекпадом:

<script>

;(function(){
    var isTouch = false //var to indicate current input type (is touch versus no touch) 
    var isTouchTimer 
    var curRootClass = '' //var indicating current document root class ("can-touch" or "")

    function addtouchclass(e){
        clearTimeout(isTouchTimer)
        isTouch = true
        if (curRootClass != 'can-touch'){ //add "can-touch' class if it not already present
            curRootClass = 'can-touch'
            document.documentElement.classList.add(curRootClass)
        }
        isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event
    }

    function removetouchclass(e){
        if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present
            isTouch = false
            curRootClass = ''
            document.documentElement.classList.remove('can-touch')
        }
    }

    document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch
    document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad
})();

</script>

Ответ 6

$("#elementwithhover").click(function() { 
  // code that makes element or parent slide or otherwise move out from under mouse. 

  $(this).clone(true).insertAfter($(this));
  $(this).remove();
});

Ответ 7

Я собирался опубликовать собственное решение, но, проверяя, что кто-то уже разместил его, я обнаружил, что @Rodney почти сделал это. Тем не менее, он пропустил последний важный момент, который сделал его единым, по крайней мере, в моем случае. Я имею в виду, что я также взял то же самое дополнение .fakeHover для добавления/удаления с помощью событий mouseenter и mouseleave, но это само по себе действует почти так же, как "подлинный" :hover. Я имею в виду: когда вы нажимаете на элемент в своей таблице, он не обнаруживает, что вы "лишили" его, тем самым сохраняя состояние "fakehover".

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

Si это мой последний код:

.fakeHover {
    background-color: blue;
}


$(document).on('mouseenter', 'button.myButton',function(){
    $(this).addClass('fakeHover');
});

$(document).on('mouseleave', 'button.myButton',function(){
    $(this).removeClass('fakeHover');
});

$(document).on('button.myButton, 'click', function(){
    $(this).mouseleave();
});

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

Ответ 8

Вы можете переопределить эффект наведения для устройств, которые не поддерживают наведение. Подобно:

.my-thing {
    color: #BADA55;
}

.my-thing:hover {
    color: hotpink;
}

@media (hover: none) {
    .my-thing {
        color: #BADA55;
    }
}

Протестировано и проверено на iOS 12

Шляпу на fooobar.com/questions/66235/... за указание на это.

Ответ 9

Мне было полезно: ссылка

function hoverTouchUnstick() {
    // Check if the device supports touch events
    if('ontouchstart' in document.documentElement) {
        // Loop through each stylesheet
        for(var sheetI = document.styleSheets.length - 1; sheetI >= 0; sheetI--) {
            var sheet = document.styleSheets[sheetI];
            // Verify if cssRules exists in sheet
            if(sheet.cssRules) {
                // Loop through each rule in sheet
                for(var ruleI = sheet.cssRules.length - 1; ruleI >= 0; ruleI--) {
                    var rule = sheet.cssRules[ruleI];
                    // Verify rule has selector text
                    if(rule.selectorText) {
                        // Replace hover psuedo-class with active psuedo-class
                        rule.selectorText = rule.selectorText.replace(":hover", ":active");
                    }
                }
            }
        }
    }
}

Ответ 10

Добавьте этот код JS на свою страницу:

document.body.className = 'ontouchstart' in document.documentElement ? '' : 'hover';

теперь в вашем CSS перед каждым наведением добавить класс зависания следующим образом:

.hover .foo:hover {}

Если устройство коснулось, класс body будет пустым, в противном случае его класс будет наводиться и применяются правила!

Ответ 11

Я мог бы добавить класс no-hover ontouchend для каждой кнопки и сделать свой CSS как > this: button: not (.no-hover): hover {background-color: синий; }, но это, вероятно, очень плохо для производительности и не обрабатывает > устройства, такие как Chromebook Pixel (который имеет как сенсорный экран, так и мышь) > правильно.

Это правильная отправная точка. Следующий шаг: применить/удалить класс nohover при следующих событиях (демонстрация с помощью jQuery)

buttonelement
 .on("touchend touchcancel",function(){$(this).addClass("nohover")})
 .on("touchstart mouseover",function({$(this).removeClass("nohover")});   

Примечание. Если вы хотите применить другие классы к элементу buttonelement, то: not (.nohover) в CSS больше не будет работать, как ожидалось. Вместо этого вам нужно добавить отдельное определение со значением по умолчанию и! Важным тегом для перезаписывания стиля наведения:    .nohover {background-color: white! important}

Это должно обрабатывать устройства, такие как Chromebook Pixel (который имеет как сенсорный экран, так и мышь) правильно! И я не думаю, что это главный убийца производительности...

Ответ 12

Решение, которое сработало для меня:

html {
   -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

Добавьте этот код в таблицу стилей.

Я хотел избавиться от серого фона, который появляется в Safari iOS при нажатии ссылки. Но, похоже, он делает больше. Теперь нажатие кнопки (с помощью псевдонима :hover) открывается сразу же! Я тестировал его только на iPad, я не знаю, будет ли он работать на других устройствах.

Ответ 13

Я думаю, что я нашел элегантное (минимальное js) решение для аналогичной проблемы:

Используя jQuery, вы можете вызвать наведение на теле (или любой другой элемент), используя .mouseover()

Поэтому я просто присоединяю этот обработчик к элементу ontouchend, например:

var unhover = function() {
  $("body").mousover();  
};
.hoverable {
  width: 100px;
  height: 100px;
  background: teal;
  cursor: pointer;
}

.hoverable:hover {
  background: pink;
}
<div class="hoverable" ontouchend={unhover}></div>

Ответ 14

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

Создайте отдельный класс зависания для эффекта наведения. По умолчанию добавьте этот класс hover к нашей кнопке.

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

Следующая проблема: что, если пользователь хочет вернуться к использованию мыши после касания кнопки? Чтобы решить эту проблему, нам нужно найти подходящий момент, чтобы добавить обратно класс hover, который мы удалили.

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

Итак, я подумал об использовании алгоритма ожидания ожидания (используя setInterval), чтобы проверить состояние зависания. После того, как состояние зависания деактивировано, мы можем добавить обратно класс hover и остановить ожидание, возвращая нас в исходное состояние, в котором пользователь может использовать либо мышь, либо touch.

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

var button = document.getElementById('myButton');

button.ontouchstart = function(e) {
  console.log('ontouchstart');
  $('.button').removeClass('button-hover');
  startIntervalToResetHover();
};

button.onclick = function(e) {
  console.log('onclick');
}

var intervalId;

function startIntervalToResetHover() {
  // Clear the previous one, if any.
  if (intervalId) {
    clearInterval(intervalId);
  }
  
  intervalId = setInterval(function() {
    // Stop if the hover class already exists.
    if ($('.button').hasClass('button-hover')) {
      clearInterval(intervalId);
      intervalId = null;
      return;
    }
    
    // Checking of hover state from 
    // http://stackoverflow.com/a/8981521/2669960.
    var isHovered = !!$('.button').filter(function() {
      return $(this).is(":hover");
    }).length;
    
    if (isHovered) {
      console.log('Hover state is active');
    } else {
      console.log('Hover state is inactive');
      $('.button').addClass('button-hover');
      console.log('Added back the button-hover class');
      
      clearInterval(intervalId);
      intervalId = null;
    }
  }, 1000);
}
.button {
  color: green;
  border: none;
}

.button-hover:hover {
  background: yellow;
  border: none;
}

.button:active {
  border: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='myButton' class='button button-hover'>Hello</button>

Ответ 15

Вы можете установить фоновый цвет в состоянии :active и дать :focus фон defaut.

если вы установили фоновый цвет через onfocus/ontouch, стиль цвета останется после состояния :focus.
Вам понадобится reset on onblur, чтобы восстановить defaut bg, когда фокус потерян.

Ответ 16

Это сработало для меня: поместите стиль зависания в новый класс

.fakehover {background: red}

Затем добавьте/удалите класс по мере необходимости

$(".someclass > li").on("mouseenter", function(e) {
  $(this).addClass("fakehover");
});
$(".someclass > li").on("mouseleave", function(e) {
  $(this).removeClass("fakehover");
});

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

Ответ 17

Основываясь на ответе Даррена Кукса, который также работает, если вы перенесите свой палец на другой элемент.

См. Найти палец элемента во время события touchhend

jQuery(function() {
    FastClick.attach(document.body);
});
// Prevent sticky hover effects for buttons on touch devices
// From https://stackoverflow.com/a/17234319
//
//
// Usage:
// <a href="..." touch-focus-fix>..</a>
//
// Refactored from a directive for better performance and compability
jQuery(document.documentElement).on('touchend', function(event) {
  'use strict';

  function fix(sourceElement) {
    var el = $(sourceElement).closest('[touch-focus-fix]')[0];
    if (!el) {
      return;
    }
    var par = el.parentNode;
    var next = el.nextSibling;
    par.removeChild(el);
    par.insertBefore(el, next);
  }

  fix(event.target);
  var changedTouch = event.originalEvent.changedTouches[0];
  // http://www.w3.org/TR/2011/WD-touch-events-20110505/#the-touchend-event
  if (!changedTouch) {
    return;
  }
  var touchTarget = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY);
  if (touchTarget && touchTarget !== event.target) {
    fix(touchTarget);
  }
});

Демо-версия Codepen

Ответ 18

Вы можете попробовать это.

JavaScript:

var isEventSupported = function (eventName, elementName) {
    var el = elementName ? document.createElement(elementName) : window;
    eventName = 'on' + eventName;
    var isSupported = (eventName in el);
    if (!isSupported && el.setAttribute) {
        el.setAttribute(eventName, 'return;');
        isSupported = typeof el[eventName] == 'function';
    }
    el = null;
    return isSupported;
};

if (!isEventSupported('touchstart')) {
    $('a').addClass('with-hover');
}

CSS

a.with-hover:hover {
  color: #fafafa;
}

Ответ 19

То, что я делал до сих пор в моих проектах, состояло в том, чтобы вернуть изменения :hover на сенсорные устройства:

.myhoveredclass {
    background-color:green;
}
.myhoveredclass:hover {
    background-color:red;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
    .myhoveredclass:hover, .myhoveredclass:active, .myhoveredclass:focus {
        background-color:green;
    }
}

Все имена классов и именованные цвета только для демонстрационных целей; -)

Ответ 20

Это работает отлично в 2 этапа.

  • Установите тег тела таким образом <body ontouchstart="">. Я не поклонник этого "взлома", но он позволяет Safari iOS мгновенно реагировать на касания. Не знаете, как это сделать, но оно работает.

  • Настройте свой сенсорный класс следующим образом:

    // I did this in SASS, but this should work with normal CSS as well
    
    // Touchable class
    .example {
    
        // Default styles
        background: green;
    
        // Default hover styles 
        // (Think of this as Desktop and larger)
        &:hover {
            background: yellow;
        }
    
        // Default active styles
        &:active {
            background: red;
        }
    
        // Setup breakpoint for smaller device widths
        @media only screen and (max-width: 1048px) {
    
            // Important!
            // Reset touchable hover styles
            // You may want to use the same exact styles as the Default styles
            &:hover {
                background: green;
            }
    
            // Important!
            // Touchable active styles
            &:active {
                background: red;
            }
        }
    }
    

Вы также можете удалить любую анимацию в своем сенсорном классе. Android Chrome выглядит немного медленнее, чем iOS.

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

Ответ 21

У меня есть хорошее решение, которым я хотел бы поделиться. Сначала вам нужно определить, есть ли у пользователя мобильный телефон, например:

var touchDevice = /ipad|iphone|android|windows phone|blackberry/i.test(navigator.userAgent.toLowerCase());

Затем просто добавьте:

if (!touchDevice) {
    $(".navbar-ul").addClass("hoverable");
}

И в CSS:

.navbar-ul.hoverable li a:hover {
    color: #fff;
}

Ответ 22

Некоторые проблемы с :hover или :hover :focus :active на мобильных устройствах могут быть вызваны отсутствием <meta name="viewport" content="width=device-width"> когда браузеры пытаются манипулировать экраном.

Ответ 23

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

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

@mixin hover-support {
  @media not all and (pointer: coarse) {
    &:hover {
      @content;
    }
  }
}

и затем я поместил все свои классы CSS под нижеупомянутый фрагмент.

   @include hover-support() {
    // Your css-classes or css that you want to apply on those devices that support hover.
   }

Например, у нас был класс для анимации значка, который использовался, когда мы наводим курсор на значок, как вы можете видеть его из css, но на сенсорном устройстве на него воздействовал липкий эффект наведения, а затем я помещал его внутрь @включите hover-support(), чтобы гарантировать, что наведение относится только к тем устройствам, которые поддерживают наведение.

@include hover-support() {
  .animate-icon {
    -webkit-transition: all 0.2s;
    transition: all 0.2s;
    &:hover {
      transform: scale(1.3);
      filter: brightness(85%);
      cursor: pointer;
    }
  }
}