web-dev-qa-db-ja.com

iPhone / iPadユーザーの:hover擬似クラスを強制的に無視することは可能ですか?

私のサイトには、_:hover_(jsなし)で展開するcssメニューがいくつかあります

これはiDevicesで半壊れた方法で機能します。たとえば、タップすると_:hover_ルールがアクティブになり、メニューが展開されますが、他の場所をタップしても_:hover_は削除されません。また、エレメント内に_:hover_ 'edのリンクがある場合は、2回タップしてリンクをアクティブにする必要があります(最初のタップが_:hover_をトリガーし、2回目のタップがリンクをトリガーします)。

touchstartイベントをバインドすることで、iPhoneでうまく動作するようになりました。

問題は、モバイルサファリがCSSから_:hover_ルールをトリガーすることを選択する場合があることです。 代わりに touchstartイベントの

CSSですべての_:hover_ルールを手動で無効にすると、モバイルサファリがうまく機能するため、これが問題であることはわかっています(ただし、通常のブラウザーでは明らかに機能しなくなります)。

ユーザーがモバイルサファリを使用しているときに、特定の要素の_:hover_ルールを動的に「キャンセル」する方法はありますか?

ここでiOSの動作を確認および比較します。 http://jsfiddle.net/74s35/3/ 注:一部のcssプロパティのみが2クリック動作をトリガーします。 display:none;背景ではありません:赤;またはテキスト装飾:下線。

92

「:hover」は、iPhone/iPad Safariでは予測不可能であることがわかりました。要素をタップすると、その要素が「:hover」になることがありますが、他の要素に移動することもあります。

とりあえず、私は体に「ノータッチ」クラスがあります。

<body class="yui3-skin-sam no-touch">
   ...
</body>

そして、「。no-touch」の下に「:hover」を持つすべてのCSSルールがあります。

.no-touch my:hover{
   color: red;
}

ページのどこかに、本文からno-touchクラスを削除するjavascriptがあります。

if ('ontouchstart' in document) {
    Y.one('body').removeClass('no-touch');
}

これは完璧に見えませんが、とにかく動作します。

76
Morgan Cheng

:hoverはここでは問題ではありません。 iOS用Safariは非常に奇妙なルールに従います。最初にmouseovermousemoveを起動します。これらのイベント中に何かが変更された場合、「クリック」および関連するイベントは発生しません。

Diagram of touch event in iOS

mouseentermouseleaveは含まれているように見えますが、チャートには指定されていません。

これらのイベントの結果として何かを変更した場合、クリックイベントは発生しません。これには、DOMツリーの上位にあるものが含まれます。たとえば、これにより、jQueryを使用してWebサイトでシングルクリックが機能しなくなります。

$(window).on('mousemove', function() {
    $('body').attr('rel', Math.random());
});

編集:説明のため、jQueryのhoverイベントにはmouseentermouseleaveが含まれています。コンテンツが変更された場合、これらは両方ともclickを防ぎます。

42
Zenexer

ブラウザ機能検出ライブラリ Modernizer には、タッチイベントのチェックが含まれています。

デフォルトの動作では、検出される各機能のhtml要素にクラスを適用します。その後、これらのクラスを使用してドキュメントのスタイルを設定できます。

タッチイベントが有効になっていない場合、Modernizrはno-touchのクラスを追加できます。

<html class="no-touch">

そして、このクラスでホバースタイルをスコープします。

.no-touch a:hover { /* hover styles here */ }

カスタムModernizrビルドをダウンロード を使用して、必要な数の機能検出を含めることができます。

適用できるクラスの例を次に示します。

<html class="js no-touch postmessage history multiplebgs
             boxshadow opacity cssanimations csscolumns cssgradients
             csstransforms csstransitions fontface localstorage sessionstorage
             svg inlinesvg no-blobbuilder blob bloburls download formdata">
18
Beau Smith

一部のデバイス(他の人が言っているように)には、タッチイベントとマウスイベントの両方があります。たとえば、Microsoft Surfaceには、タッチスクリーン、トラックパッド、およびスタイラスがあり、画面の上にホバーするとホバーイベントが実際に発生します。

「タッチ」イベントの存在に基づいて_:hover_を無効にするソリューションは、Surfaceユーザー(および他の多くの同様のデバイス)にも影響します。多くの新しいラップトップはタッチ式であり、タッチイベントに応答します。したがって、ホバリングを無効にすることは非常に悪い習慣です。

これはSafariのバグです。この恐ろしい動作を正当化する理由はまったくありません。 iOS Safariのバグが明らかに長年存在しているため、iOS以外のブラウザを妨害することを拒否します。来週iOS8でこれを修正することを本当に望んでいますが、それまでは...

私の解決策

すでにModernizrを使用することを提案している人もいますが、Modernizrでは独自のテストを作成できます。ここで基本的にやっていることは、_:hover_をサポートするブラウザーのアイデアをModernizrテストに「抽象化」して、コード全体でif (iOS)をハードコーディングせずに使用できることです。

_ Modernizr.addTest('workinghover', function ()
 {
      // Safari doesn't 'announce' to the world that it behaves badly with :hover
      // so we have to check the userAgent  
      return navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? false : true;
 });
_

その後、CSSはこのようなものになります

_html.workinghover .rollover:hover 
{
    // rollover css
}
_

IOSでのみ、このテストは失敗し、ロールオーバーが無効になります。

このような抽象化の最も良い部分は、特定のAndroidで壊れている場合、またはiOS9で修正されている場合は、テストを変更するだけです。

17
Simon_Weaver

FastClickライブラリ をページに追加すると、モバイルデバイスのすべてのタップが(ユーザーがクリックした場所に関係なく)クリックイベントに変換されるため、モバイルデバイスのホバーの問題も修正する必要があります。例としてフィドルを編集しました: http://jsfiddle.net/FvACN/8/

ページにfastclick.min.js libを含めて、次の方法でアクティブにします。

FastClick.attach(document.body);

副次的な利点として、モバイルデバイスが受ける厄介な300ミリ秒のonClick遅延も削除されます。


FastClickを使用すると、サイトにとって重要な場合も重要でない場合もありますが、いくつかの小さな影響があります。

  1. ページのどこかをタップし、上にスクロールし、下にスクロールして、最初に配置した位置とまったく同じ位置で指を離すと、FastClickはそれが「クリック」であると解釈します。少なくとも、私が現在使用しているFastClickのバージョン(1.0.0)での動作です。そのバージョン以降、誰かが問題を修正した可能性があります。
  2. FastClickは、誰かが「ダブルクリック」する機能を削除します。
16
Troy

JS、CSSクラス、ビューポートのチェックなしのより良いソリューション:インタラクションメディア機能を使用できます (Media Queries Level 4)

このような:

@media (hover) {
  // properties
  my:hover {
    color: red;
  }
}

iOS Safari サポート

詳細: https://www.jonathanfielding.com/an-introduction-to-interaction-media-features/

13
Estevão Lucas

基本的に3つのシナリオがあります。

  1. ユーザーonlyにはマウス/ポインターデバイスがあり、:hoverをアクティブにできます
  2. ユーザーonlyにはタッチスクリーンがあり、:hover要素をアクティブにできません
  3. ユーザーはbothタッチスクリーンとポインターデバイスを持っています

最初に受け入れられた答えは、ユーザーがeitherポインターまたはタッチスクリーンを持っている最初の2つのシナリオのみが可能な場合に非常に効果的です。 OPが4年前に質問したとき、これは一般的でした。一部のユーザーは、Windows 8およびSurfaceデバイスが3番目のシナリオをより可能性の高いものにしていることを指摘しています。

(@Zenexerで詳述されているように)タッチスクリーンデバイス上でホバリングできないという問題に対するiOSソリューションは賢いですが、簡単なコードの誤動作を引き起こす可能性があります(OPに記載されています)。タッチスクリーンデバイスでのみホバーを無効にするということは、タッチスクリーン対応の代替手段をコーディングする必要があることを意味します。ユーザーがポインターとタッチスクリーンの両方を持っていることを検出すると、さらに水が濁ります(@Simon_Weaverによる説明)。

この時点で、最も安全な解決策は、ユーザーがWebサイトと対話できる唯一の方法として:hoverを使用しないことです。ホバー効果は、リンクまたはボタンがアクション可能であることを示す良い方法ですが、ユーザーがWebサイトでアクションを実行するために要素をホバーする必要はありません。

タッチスクリーンを念頭に置いて「ホバー」機能を再考する は、代替のUXアプローチについて適切に議論しています。そこでの答えによって提供されるソリューションは次のとおりです。

  • ホバーメニューを直接アクションに置き換える(常に表示されるリンク)
  • オンホバーメニューをオンタップメニューに置き換える
  • 大量のホバーコンテンツを別のページに移動する

今後、これはおそらくすべての新しいプロジェクトにとって最適なソリューションになるでしょう。受け入れられた答えはおそらく2番目に良い解決策ですが、ポインタデバイスも持っているデバイスを考慮してください。 iOSの:hoverハックを回避するためだけにデバイスにタッチスクリーンがある場合、機能を削除しないように注意してください。

4
thirdender

.cssのJQueryバージョンは、すべてのホバールールに.no-touch .my-element:hoverを使用します。これには、JQueryと次のスクリプトが含まれます

function removeHoverState(){
    $("body").removeClass("no-touch");
}

次に、bodyタグにclass = "no-touch" ontouchstart = "removeHoverState()"を追加します

ontouchstartが起動すると、すべてのホバー状態のクラスが削除されます

1
Greg

答えてくれた@Morgan Chengに感謝しますが、「touchstart」(@ Timothy Perezから取られたコード- answer )ただし、これにはjQuery 1.7以降が必要です

  $(document).on({ 'touchstart' : function(){
      //do whatever you want here
    } });
0
3Gee

Zenexerが提供する応答を考えると、追加のHTMLタグを必要としないパターンは次のとおりです。

jQuery('a').on('mouseover', function(event) {
    event.preventDefault();
    // Show and hide your drop down nav or other elem
});
jQuery('a').on('click', function(event) {
    if (jQuery(event.target).children('.dropdown').is(':visible') {
        // Hide your dropdown nav here to unstick
    }
});

このメソッドは、最初にマウスオーバーを起動し、次にクリックします。

0
veryphatic

私は、タッチのホバーを無効にすることが道であることに同意します。

ただし、CSSを書き直す手間を省くために、@supports not (-webkit-overflow-scrolling: touch) {}:hoverアイテムをラップするだけです。

.hover, .hover-iOS {
  display:inline-block;
  font-family:arial;
  background:red;
  color:white;
  padding:5px;
}
.hover:hover {
  cursor:pointer;
  background:green;
}

.hover-iOS {
  background:grey;
}

@supports not (-webkit-overflow-scrolling: touch) {
  .hover-iOS:hover {
    cursor:pointer;
    background:blue;
  }

}
<input type="text" class="hover" placeholder="Hover over me" />

<input type="text" class="hover-iOS" placeholder="Hover over me (iOS)" />
0
Jordan Young

タッチが利用できないときにホバー効果を持たせる代わりに、タッチイベントを処理するシステムを作成しました。これにより、問題が解決しました。最初に、「タップ」(「クリック」に相当)イベントをテストするためのオブジェクトを定義しました。

touchTester = 
{
    touchStarted: false
   ,moveLimit:    5
   ,moveCount:    null
   ,isSupported:  'ontouchend' in document

   ,isTap: function(event)
   {
      if (!this.isSupported) {
         return true;
      }

      switch (event.originalEvent.type) {
         case 'touchstart':
            this.touchStarted = true;
            this.moveCount    = 0;
            return false;
         case 'touchmove':
            this.moveCount++;
            this.touchStarted = (this.moveCount <= this.moveLimit);
            return false;
         case 'touchend':
            var isTap         = this.touchStarted;
            this.touchStarted = false;
            return isTap;
         default:
            return true;
      }
   }
};

次に、イベントハンドラーで次のような操作を行います。

$('#nav').on('click touchstart touchmove touchend', 'ul > li > a'
            ,function handleClick(event) {
               if (!touchTester.isTap(event)) {
                  return true;
               }

               // touch was click or touch equivalent
               // nromal handling goes here.
            });
0
donut