Як запобігти накладенням кнопок на кнопках на сенсорних пристроях

Я створив карусель із попередньою та наступною кнопками, які завжди видно. Ці кнопки мають статус наведення, вони стають синіми. На сенсорних пристроях, як iPad, стані наведення є липкими, тому кнопка залишається синьою після натискання на неї. Я цього не хочу.

  • Я міг би додати клас no-hover класу onuchend для кожної кнопки та зробити мій CSS на зразок цього: кнопка : не (.no-hover): наведіть курсор {фоновий колір: синій; } , але це, ймовірно, досить погано для продуктивності, та ні керувати пристроями, такими як Chromebook Pixel (у якому є обоє сенсорний екран та миша).

  • Я міг би додати touch класу до documentElement і зробити мою CSS на зразок цього: html: not (.touch) button: hover {background-color: blue; } , але це також не спрацьовує прямо на пристроях із дотиком та а миша.

Я б вважав за краще видалити стан наведення> onuchend . Але це здається не таким, що можливо. Фокусування на інший елемент не видаляє стан наведення. Вимикає інший елемент вручну, але я не можу викликати це в JavaScript.

Всі знайдені мені рішення здаються недосконалими. Чи є ідеальним рішенням?

107

19 Відповіді

You can remove the hover state by temporarily removing the link from the DOM. See 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 ви маєте:

test
47
додано
Використання setTimeout іноді призводить до миготіння. Схоже, що код працює без нього, тому його можна видалити.
додано Автор Kevin Borders, джерело
Чудово - я так довго шукала хорошого рішення. Дякую!
додано Автор mdunisch, джерело
@SjoerdVisscher Я вставив його в. StackOverflow любить код, щоб бути у відповідь, оскільки посилання можуть піти. (І в цьому випадку потрібно було не просто натиснути кнопку, а потім переглянути джерело, а також визначити, які біти - це техніка, про яку йде мова.)
додано Автор Darren Cook, джерело
@KevinBorders так, на деяких пристроях час затримки між видаленням та повторним вставленням елемента може бути дуже помітним. На жаль, я знайшов на своєму пристрої Android 4.4, що робить це без setTimeout не працює.
додано Автор Rodney, джерело
Добре зроблено Працює як чарівність.
додано Автор Nicolas Bouvrette, джерело
Будь ласка, розгляньте можливість додати мінімальний демонстраційний код до вашої відповіді. Дякую! stackoverflow.com/help/how-toanswer
додано Автор jtheletter, джерело
Це виправлення, коли його вставляють у моє додаток, працюють на моєму Nexus 5, запущеному Android 4.4.2 (з затримкою 100 мс) у Chrome, але не на моєму iOS Ipod Touch в Safari
додано Автор Anthony Astige, джерело
@janaspage Саме тут є посилання. Я перетворив це в відповідь на вікі в спільноті, тому, якщо ви хочете поставити його тут, йдіть прямо вперед!
додано Автор Sjoerd Visscher, джерело
@ Chris Хороший момент, я змінив приклад, щоб встановити обробник onclick в події onuchend.
додано Автор Sjoerd Visscher, джерело
Ідея цікава, але я виявив, що вона трохи незграбна і не підтримується на всіх сенсорних пристроях. Я б вважав за краще менш інвазивне рішення, засноване на виявленні сенсорної підтримки та чистому css, наприклад stackoverflow.com/a/39787268/885464
додано Автор Lorenzo Polidori, джерело
@ DarrenCook Але чи є гарна ідея видалити елемент і повторно додати його? Я думаю, це призведе до "janking" і конфлікту з 60-кадровицевими кадрами вертикального масла плавних потреб програми.
додано Автор Ethan, джерело
Це зламане для мене в iOS10 для мене. Чи має хтось інший такий же випадок?
додано Автор theRyanMark, джерело
Оце Так! Я повинен був знати, що Шьоред отримає відповідь. :) Є один невеликий недолік, хоча: після натискання мишею, ефект наведення також зникає. Як правило, це не відбувається, тому це виглядає трохи дивно. Використання onuchend замість onclick не працює, хоча. :(
додано Автор Chris, джерело

Після реалізації CSS Media Queries Level 4 ви зможете це зробити:

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

Або англійською мовою: "Якщо веб-переглядач підтримує правильне/справжнє/реальне/неемульоване висунення (наприклад, має основний пристрій вводу миші), то застосовуйте цей стиль, коли кнопки вирівнюються".

Оскільки ця частина медіа-запитів 4-го рівня досі була реалізована тільки в Chrome, то я написав поліфайл , щоб вирішити цю проблему. Використовуючи це, ви можете перетворити вищевказаний футуристичний CSS у:

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

(Варіація методу .no-touch ) І тоді, використовуючи який-небудь клієнтський JavaScript з тієї самої поліфайлу, яка визначає підтримку курсору, ви можете переключити наявність my-true- наведіть клавішу відповідно:

$(document).on('mq4hsChange', function (e) {
    $(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});
43
додано
Дякую! Для моїх цілей це, як видається, підтримується в більшості браузера caniuse.com/#search=media%20queries і працює блискуче, спасибі!
додано Автор ragamufin, джерело
Вам потрібно подивитися на caniuse.com/#feat=css-media-interaction замість цього ви побачите, що вона не підтримується в Firefox і IE11 :(, тому вам потрібна поліфайла
додано Автор CherryDT, джерело

Це загальна проблема без ідеального рішення. Поведінка наведення є корисним за допомогою миші і в основному збитковим із дотиком. Проблеми, що складають проблему, - це пристрої, які підтримують сенсорну та мишу (одночасно, не менше!), Як 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
додано
Хороша відповідь. Це аналогічне рішення: stackoverflow.com/a/39787268/885464
додано Автор Lorenzo Polidori, джерело
Чудово, це працювало для мене, на відміну від спільноти wiki/Darren Cook.
додано Автор Jannik, джерело

Використовуючи 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
додано
Я вважаю, що одним із способів цього буде використання як Modernizr, так і бібліотеки, такої як мобільний -detect.js , щоб переконатися, що це телефон або планшет.
додано Автор Gavin, джерело
Проблема в тому, що тепер, коли пристрої з підтримкою миші мають сенсорні екрани, це все більше, ви більше не можете покладатися на цей метод. Однак я не бачу багато іншого способу зробити це ... це досить дилема
додано Автор dudewad, джерело
Це найкраща відповідь IMO, але приклад невірний. Вона показує ту ж саму ситуацію наведення для дотиків і пристроїв без дотику. Якщо в тегу html присутній .no-touch , слід застосувати лише режим наведення. Інакше ця відповідь отримає мою печатку схвалення.
додано Автор morrisbret, джерело

З 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
додано
Це найкращий спосіб знайти, і якщо збільшити таймер до 1000 мс, ви також можете покрити довге натискання, дивіться android "> тут . Великі речі!
додано Автор lowtechsun, джерело
$("#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
додано
дивовижний bro, 10x
додано Автор Kaloyan Stamatov, джерело
Ви можете покращити цю відповідь, використовуючи для() замість клацання (). Видаляючи елемент з DOM і повторно приєднуючи його, я втратив мої кліки. Коли я переписав мою функцію з включенням, прив'язка до елемента, потім видалення і додавання працювали. Наприклад: `$ ('body'). On ('click', '# elementwithhover', function() {// клон, instafter, remove}); Я буду голосувати, якщо ви внесете цю зміну.
додано Автор Will Lanni, джерело
клон істинний зберігає подію кліків на новій elemend, що займає місце елемента зависним курсором. початковий елемент видаляється з дому після його клонування.
додано Автор Verard Sloggett, джерело

Я збираюся розмістити моє власне рішення, але, перевіряючи, чи хтось вже опублікував його, я виявив, що @ Родні практично зробив це. Проте, він пропустив це останнє ключове, що зробило його приємним, принаймні у моєму випадку. Я маю на увазі, я теж взяв такий же додавання/видалення класу .fakeHover за допомогою визначення mouseenter та mouseleave , але тільки для цього per se , діє майже так само, як "справжній" : 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 . Багато в чому, якби ви натиснули і швидко вивели покажчик з кнопки. Але в моєму випадку я можу з цим жити.

2
додано

Додайте цей JS-код на свою сторінку:

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

Тепер у вашому CSS перед кожним наведенням клацніть hover, подібно до цього:

.hover .foo:hover {}

Якщо пристрій є дотиком, клас тіла буде порожній, інакше його клас буде наведене на вітер, і правила застосовуються!

1
додано
Для мене це не допомогло, але це зробило: document.body.className = 'inuchstart' у вікні? '': 'наведіть курсор';
додано Автор drskullster, джерело

Ось що я придумав поки що після вивчення решти відповідей. Він повинен мати можливість підтримувати тільки сенсорний, лише мишачий або гібридний користувачів.

Створіть окремий клас наведення для ефекту наведення. За замовчуванням додайте цей клас hover на нашу кнопку.

Ми не хочемо виявити наявність сенсорної підтримки та відключити всі наведені ефекти з самого початку. Як згадували інші, гібридні пристрої набувають популярності; люди можуть мати сенсорну підтримку, але хочуть використовувати мишу і навпаки. Тому видаляйте лише клацніть, коли користувач фактично торкається кнопки.

Наступна проблема полягає в тому, що, якщо користувач хоче повернутися до використання миші після натискання кнопки? Щоб вирішити це, нам потрібно знайти відповідний момент для того, щоб додати назад клас наведення, який ми видалили.

Однак, ми не можемо додати його негайно після видалення, оскільки стан наведення залишається активним. Можливо, ми не хочемо знищити і відтворити всю кнопку.

Отже, я думав про використання алгоритму зайнятого очікування (використовуючи setInterval) для перевірки стану наведення. Після того, як стан витягу деактивовано, ми можемо потім додати назад клас наведення та зупинити очікуване заняття, повернувши нас назад до початкового стану, де користувач може використовувати мишу або торкнутися.

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

<div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"> <div class="snippet-code">

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

Редагування: інший підхід, який я намагався, це викликати e.preventDefault() всередині onuchstart або onuchend. Здається, що зупинка ефекту наведення, коли торкається кнопки, але також припиняє натискання кнопки анімації та запобігає виклику функції onclick при натисненні кнопки, тому вам слід викликати ці дії вручну у обробнику на onuchstart або onuthend. Не дуже чисте рішення.

1
додано

It was helpful for me: link

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
додано
Вам потрібно один раз викликати hoverTouchUnstick() від глобального обробника процесу onuchstart. І це рішення буде чудово працювати.
додано Автор Jusid, джерело

I could add a no-hover class ontouchend for each button, and make my CSS like >this: button:not(.no-hover):hover { background-color: blue; } but that's probably quite bad for performance, and doesn't handle >devices like the Chromebook Pixel (which has both a touchscreen and a mouse) >correctly.

Це правильна відправна точка. Наступний крок: застосувати/видалити клас nohover на наступні події (демонстрація з jQuery)

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

Зверніть увагу: якщо ви хочете застосувати інші класи до тексту, то: не (.nohover) в CSS більше не буде працювати, як очікувалося. Замість цього вам слід додавати окреме визначення із значенням за замовчуванням та! Важливим тегом, щоб переписати стиль hover:      .nohover {фоновий колір: білий! важливо}

Це також має справу з такими пристроями, як Chromebook Pixel (який має як сенсорний екран, так і мишу). І я не думаю, що це найважливіший спектакль-вбивця ...

1
додано

На основі відповіді Darren Cookies, яка також працює, якщо ви перенесли палець на інший елемент.

See Find element finger is on during a touchend event

jQuery(function() {
    FastClick.attach(document.body);
});
// Prevent sticky hover effects for buttons on touch devices
// From https://stackoverflow.com/a/17234319
//
//
// Usage:
// ..
//
// 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);
  }
});

Demo Codepen

0
додано

Це ідеально працює в два кроки.

  1. Set your body tag to be like this <body ontouchstart="">. I'm not a fan of this "hack", but it allows Safari on iOS to react to touches instantly. Not sure how, but it works.

  2. Set up your touchable class like this:

    // 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;
            }
        }
    }
    

Можливо, ви захочете видалити будь-яку анімацію на своєму класі, який можна торкатися. Chrome Chrome виглядає дещо повільніше, ніж iOS.

Це також призведе до активного стану, що застосовується, якщо користувач прокручує сторінку при дотику до свого класу.

0
додано

Це попрацювало для мене: покладіть стиль наведення в новий клас

.fakehover {background: red}

Потім додайте/видаліть клас, коли це потрібно

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

Повторіть для touchstart та вибирайте події. Або будь-які події, які вам подобаються, щоб отримати потрібний результат, наприклад, я хотів, щоб ефект наведення вказував на сенсорний екран.

0
додано

Ви можете встановити фоновий колір у стані : active і надати фону : focus .

якщо ви встановите фоновий колір за допомогою onfocus/onvouch , стиль кольору залишається колись стан : focus . Вам також потрібно скинути значення onblur , щоб відновити defaut bg при втраті фокусу.

0
додано
: наведіть курсор на і: активний може отримати такий самий CSS, це на: фокус у вас є проблема. Насправді, якщо ви встановлюєте фоновий колір за допомогою onfocus, стиль кольору залишається, коли фокус зник. вам потрібно також скинути на onlur, щоб відновити defaut bg
додано Автор G-Cyr, джерело
Ви протестували це самі? Тому що це не працює.
додано Автор Chris, джерело
Але я хотів би зберегти ефект наведення для користувачів миші.
додано Автор Chris, джерело

Ви можете спробувати таким чином.

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
додано

Те, що я зробив до цих пір у моїх проектах, було повернути зміни : 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
додано

Рішення, яке працювало для мене:

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

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

Я хотів позбутися сірого тла, що з'являється на пристрої iOS Safari при натисканні посилання. Але це, схоже, більше. Тепер натискання кнопки (з псевдокласом : hover ) відкривається відразу! Я протестував його лише на iPad, не знаю, чи працюватиме він на інших пристроях.

0
додано

Я думаю, що знайшов елегантне (мінімальне JS) рішення для подібної проблеми:

Using jQuery, you can trigger hover on body (or any other element), using .mouseover()

Тому я просто приєднаю цей обробник до події onuchend елемента, наприклад:

<div class="snippet" data-lang="js" data-hide="false"> <div class="snippet-code">

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

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

Це, однак, видаляє лише: наведіть псевдоклас із елемента після того, як інше торкнеться події, наприклад, проведіть пальцем або іншим торканням

0
додано
Для фрагмента коду квадрат все ще залишається рожевим після того, як я торкнувся його. Я думаю, ви не вирішили проблему, задану в цьому питанні?
додано Автор Kevin Lee, джерело
Це рішення не працює. По-перше, ви використали неправильний метод для запуску події, ви повинні використовувати .trigger() . По-друге, не працює на мобільному сафарі в будь-якому випадку.
додано Автор xHocquet, джерело
ІТ КПІ - JavaScript
ІТ КПІ - JavaScript
504 учасників

співтовариство javascript розробників в Telegram