it-swarm.com.ru

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

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

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

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

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

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

116
Chris

Вы можете удалить состояние наведения, временно удалив ссылку из 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>
49
Sjoerd Visscher

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

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

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

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

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

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

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

Это обычная проблема без идеального решения. Поведение при наведении курсора полезно с помощью мыши и в основном вредно при касании. Усугубляют проблему устройства, поддерживающие сенсорное управление и мышь (одновременно, не меньше!), Такие как 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 (например, стилус).

24
Tim Medora

С помощью 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, потому что он работает как на мобильных устройствах, так и на компьютерах.

10
rachel

Из 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's 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
coco puffs
$("#elementwithhover").click(function() { 
  // code that makes element or parent slide or otherwise move out from under mouse. 

  $(this).clone(true).insertAfter($(this));
  $(this).remove();
});
5
Verard Sloggett

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

Я также просто прослушал 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. Как если бы вы нажали и быстро вынули указатель из кнопки. Но в моем случае я могу жить с этим.

2
Pere

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

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

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

.hover .foo:hover {}

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

1
Ali

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

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

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

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

1
Leon Vogler

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

Используя jQuery, вы можете запустить hover на теле (или любом другом элементе), используя .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>

Это, однако, удаляет псевдокласс: hover из элемента только после запуска какого-либо другого события касания, например, удара или другого касания 

1
Ian Averno

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

.my-thing {
    color: #BADA55;
}

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

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

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

Шляпа для https://stackoverflow.com/a/50285058/178959 для указания на это.

1
OrganicPanda

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

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

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

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

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

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

Я знаю, что ожидание не так уж и здорово, но я не уверен, что есть подходящее событие. Я подумал добавить его обратно в событие 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>

Правка: Другой подход, который я попробовал, это вызвать e.preventDefault() в ontouchstart или ontouchend. Кажется, что он останавливает эффект наведения при нажатии кнопки, но он также останавливает анимацию нажатия кнопки и предотвращает вызов функции onclick при касании кнопки, поэтому вы должны вызывать их вручную в обработчике ontouchstart или ontouchend. Не очень чистое решение.

1
Kevin Lee

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

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

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

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

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

1
Sascha Hendel

Это было полезно для меня: ссылка

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");
                    }
                }
            }
        }
    }
}
1
mineroot

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

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;
}
0
Lorenzo Polidori

Вы можете установить цвет фона в состоянии :active и задать :focus фон по умолчанию.

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

0
G-Cyr

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

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

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 Demo

0
jantimon

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

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;
}
0
Michał Reszka

То, что я делал до сих пор в своих проектах, состояло в том, чтобы отменить изменения :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;
    }
}

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

0
z00l

Это прекрасно работает в 2 этапа.

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

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

    // 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.

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

0
Michael

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

0
GEkk

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

.fakehover {background: red}

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

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

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

0
Rodney