web-dev-qa-db-ja.com

タッチデバイスのボタンのホバー効果を防ぐ方法

前と次のボタンが常に表示されるカルーセルを作成しました。これらのボタンにはホバー状態があり、青に変わります。 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でそれをトリガーすることはできません。

私が見つけた解決策はすべて不完全なようです。完璧な解決策はありますか?

123
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>
52
Sjoerd Visscher

CSS Media Queries Level 4 が実装されると、これを実行できるようになります。

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

または英語の場合:「ブラウザが適切な/ true/real/non-emulatedホバリングをサポートしている場合(マウスのようなプライマリ入力デバイスがある場合)、buttonsがホバーされるときにこのスタイルを適用します。」

メディアクエリレベル4のこの部分は、これまでのところ最先端のChromeでのみ実装されているため、これに対処するために ポリフィルを作成しました を作成しました。これを使用して、上記の未来的な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);
});
52
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タッチ入力(スタイラスなど)がミックスに追加されたときに機能することを保証できません。

26
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

From モバイルでスティッキーホバーを処理する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

ホバーをサポートしていないデバイスのホバー効果をオーバーライドできます。のような:

.my-thing {
    color: #BADA55;
}

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

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

IOS 12でテストおよび検証済み

これを指摘してくれた https://stackoverflow.com/a/50285058/178959 へのヒント.

2
OrganicPanda

私は自分の解決策を投稿するつもりでしたが、誰かが既にそれを投稿しているかどうかを確認すると、@ Rodneyがほぼそれをやったことがわかりました。しかし、彼は、少なくとも私の場合、それを不公平にする最後の重要な要素を逃しました。私も、mouseenterおよびmouseleaveイベント検出を介して同じ.fakeHoverクラスの追加/削除を行いましたが、それだけでper)は、ほぼ「本物の」: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 {}

デバイスがタッチされている場合、ボディクラスは空になります。そうでない場合、そのクラスはホバーし、ルールが適用されます!

1
Ali

私のために働いた解決策:

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

このコードをスタイルシートに追加します。

リンクがクリックされたときにiOS Safariに表示される灰色の背景を取り除きたいと思いました。しかし、それ以上のことをするようです。ボタン(:hover疑似クラス!)をクリックするとすぐに開きます! iPadでしかテストしていません。他のデバイスで動作するかどうかはわかりません。

1
Leon Vogler

私は同様の問題に対するエレガントな(最小js)ソリューションを見つけたと思います:

JQueryを使用すると、.mouseover()を使用して、ボディ(またはその他の要素)でホバーをトリガーできます。

そのため、次のようにthisハンドラーを要素の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

これは、残りの答えを研究した後に私がこれまでに思いついたものです。タッチのみ、マウスのみ、またはハイブリッドのユーザーをサポートできる必要があります。

ホバー効果用に別のホバークラスを作成します。デフォルトでは、このホバークラスをボタンに追加します。

タッチサポートの存在を検出し、最初からすべてのホバー効果を無効にしたくありません。他の人が述べたように、ハイブリッドデバイスは人気を集めています。人々はタッチをサポートしているかもしれませんが、マウスを使用したい、またはその逆をしたい場合があります。したがって、ユーザーが実際にボタンに触れたときにのみ、ホバークラスを削除してください。

次の問題は、ユーザーがボタンに触れた後にマウスの使用に戻りたい場合はどうなるでしょうか?それを解決するには、削除したホバークラスを追加する適切な瞬間を見つける必要があります。

ただし、ホバー状態はまだアクティブなので、削除した後すぐに追加することはできません。ボタン全体を破棄して再作成したくない場合もあります。

そこで、ホバー状態を確認するためにビジー待機アルゴリズム(setIntervalを使用)を使用することを考えました。ホバー状態が非アクティブになったら、ホバークラスを追加してビジー待機を停止し、ユーザーがマウスまたはタッチを使用できる元の状態に戻すことができます。

忙しく待つのはそれほど素晴らしいことではありませんが、適切なイベントがあるかどうかはわかりません。 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>

編集:私が試みた別のアプローチは、ontouchstartまたはontouchend内でe.preventDefault()を呼び出すことです。ボタンがタッチされるとホバー効果が停止するように見えますが、ボタンクリックアニメーションも停止し、ボタンがタッチされたときにonclick関数が呼び出されないようにするため、ontouchstartまたはontouchendハンドラーでそれらを手動で呼び出す必要があります。非常にきれいなソリューションではありません。

1
Kevin Lee

各ボタンにノーホバークラスontouchendを追加し、CSSを> this:button:not(.no-hover):hover {background-color:blue; }しかし、それはおそらくパフォーマンスにとって非常に悪く、Chromebook Pixel(タッチスクリーンとマウスの両方を搭載)などのデバイスを正しく処理しません。

これが正しい出発点です。次のステップ:次のイベントでnohoverクラスを適用/削除します(jQueryによるデモ)

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

注:他のクラスをボタン要素に適用する場合、CSSの:not(.nohover)は期待どおりに機能しなくなります。代わりに、デフォルト値と!importantタグを使用した別の定義を追加して、ホバースタイルを上書きする必要があります。nohover {background-color:white!important}

これで、Chromebook Pixel(タッチスクリーンとマウスの両方)のようなデバイスも正しく処理できるはずです!そして、私はこれが主要なパフォーマンスキラーであるとは思いません...

1
Sascha Hendel

役に立ちました: 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
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

:active状態でbackground-colorを設定し、:focusにデフォルトの背景を与えることができます。

onfocus/ontouchを介してbackground-colorを設定した場合、:focus状態がなくなると、カラースタイルは残ります。
フォーカスが失われたときにdefaut bgを復元するには、onblurもリセットする必要があります。

0
G-Cyr

Darren Cooksの回答に基づいており、別の要素の上に指を移動した場合にも機能します。

タッチエンドイベント中に要素の指を見つける を参照してください

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="">のように設定します。私はこの「ハック」のファンではありませんが、iOSのSafariがタッチに即座に反応できるようにします。方法はわかりませんが、動作します。

  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

私は同様の問題を経験しました、私のアプリケーションはすべての画面サイズと互換性がありましたdesktop-screen-size /マウスベースのデバイスでホバー効果がたくさんありましたが、後でタッチベースのデバイスに気付きましたスティッキーホバーと呼ばれる状態を引き起こしており、タッチベースのデバイスユーザーにとってアプリが適切に動作するためのハードルでした。

アプリでSCSSを使用していました。そして、タッチベースのデバイスの世話をするためにmixinを定義しました。

@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からわかるようにアイコンにカーソルを合わせるとトリガーされていましたが、タッチベースのデバイスではスティッキーホバー効果の影響を受けていたため、内部に配置しました- @ include 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;
    }
  }
}
0
Divyanshu Rawat

これは私のために働いた:ホバースタイリングを新しいクラスに入れる

.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