web-dev-qa-db-ja.com

モバイルブラウザでホバー効果を無効にする

私は、デスクトップとタブレットの両方から使用することを意図したWebサイトを書いています。デスクトップからアクセスしているとき、画面のクリック可能な領域を:hover効果(異なる背景色など)で点灯させたいです。タブレットではマウスがないので、ホバーしたくないですエフェクト。

問題は、タブレット上で何かをタップすると、ブラウザには明らかに「見えないマウスカーソル」があり、それがタップした場所に移動してそこにとどまることです。他の何かをタップするまで効果をホバーします。

マウスを使用しているときにホバー効果を得ることができますが、タッチスクリーンを使用しているときはホバー効果を抑制できますか?

誰かがそれを提案することを考えていた場合、私はユーザーエージェントスニッフィングを使いたくありません。同じデバイスにタッチスクリーンとマウスの両方を搭載することもできます(今日はそれほど一般的ではないかもしれませんが、将来的にはもっと一般的です)。私はこのデバイスには興味がありません。現在どのように使用されているか、つまりマウスまたはタッチスクリーンに興味があります。

既にtouchstarttouchmove、およびtouchendイベントをフックし、それらすべてでpreventDefault()を呼び出してみました。これにより、「見えないマウスカーソル」が時々抑制されます。しかし、2つの異なる要素間をすばやく前後にタップすると、数回タップすると「マウスカーソル」の移動が開始され、とにかくホバーエフェクトが点灯します-私のpreventDefaultは常に尊重されるわけではありません。必要な場合を除き、詳細を説明しません。それが正しいアプローチであるかどうかさえわかりません。誰でも簡単な修正があれば、私はすべて耳です。


Edit:これはbog-standard CSS :hoverで再現できますが、参考のために簡単に再現します。

<style>
  .box { border: 1px solid black; width: 150px; height: 150px; }
  .box:hover { background: blue; }
</style>
<div class="box"></div>
<div class="box"></div>

いずれかのボックスの上にマウスを置くと、青色の背景が表示されます。しかし、いずれかのボックスをタップすると、青い背景も表示されます。これは、私が防止しようとしていることです。

また、上記を実行し、jQueryのマウスイベントをフックするサンプル here を投稿しました。これを使用して、タップイベントがmouseentermousemove、およびmouseleaveも起動することを確認できます。

110
Joe White

ホバー効果によってページのコンテンツが変わるという質問からお聞きします。その場合、私のアドバイスは次のとおりです。

  • touchstartおよびmouseenterにホバー効果を追加します。
  • mouseleavetouchmove、およびclickのホバー効果を削除します。

または、コンテンツを変更しないようにページを編集できます。

バックグラウンド

マウスをシミュレートするために、ユーザーがタッチスクリーン(iPadなど)でタッチして指を離すと、Webkitモバイルなどのブラウザーは次のイベントを起動します(ソース: Touch And Mouse on html5rocks.com):

  1. touchstart
  2. touchmove
  3. touchend
  4. ブラウザがダブルタップではなくシングルタップであることを確認する300ミリ秒の遅延
  5. mouseover
  6. mouseenter
    • mouseovermouseenter、またはmousemoveイベントがページコンテンツを変更する場合、以下のイベントは発生しません。
  7. mousemove
  8. mousedown
  9. mouseup
  10. click

単にマウスイベントをスキップするようにウェブブラウザに指示することは不可能と思われます。

さらに悪いことに、マウスオーバーイベントがページコンテンツを変更した場合、 Safari Webコンテンツガイド-イベントの処理 で説明されているように、クリックイベントは発生しません。特に、One -指イベント。まさに「コンテンツの変更」とは、ブラウザーとバージョンによって異なります。 iOS 7.0の場合、背景色の変更はコンテンツの変更ではない(またはもはやない)ことがわかりました。

ソリューションの説明

要点をまとめると:

  • touchstartおよびmouseenterにホバー効果を追加します。
  • mouseleavetouchmove、およびclickのホバー効果を削除します。

touchendにはアクションがないことに注意してください!

これは、マウスイベントに対して明らかに機能します。mouseenterおよびmouseleavemouseoverおよびmouseoutのわずかに改善されたバージョン)が起動され、ホバーを追加および削除します。

ユーザーが実際にリンクをclicksした場合、ホバー効果も削除されます。これにより、ユーザーがWebブラウザーの戻るボタンを押すと、確実に削除されます。

これは、タッチイベントでも機能します。タッチスタートでは、ホバー効果が追加されます。 touchendでは '' 'not' ''が削除されます。これはmouseenterに再び追加され、これによりコンテンツの変更が発生しないため(既に追加されている)、clickイベントも発生し、ユーザーが再度クリックする必要なくリンクがたどられます!

ブラウザーがtouchstartイベントとclickの間に持つ300ミリ秒の遅延は、ホバー効果がこの短い時間の間に表示されるため、実際に有効に使用されます。

ユーザーがクリックをキャンセルすることを決定した場合、指を動かすと通常どおりにキャンセルされます。通常、これはmouseleaveイベントが発生しないため問題であり、ホバー効果はそのまま残ります。ありがたいことに、これはtouchmoveのホバー効果を削除することで簡単に修正できます。

それでおしまい!

たとえば、 FastClickライブラリ を使用して300ミリ秒の遅延を削除することもできますが、これはこの質問の範囲外です。

代替ソリューション

私は、次の代替案で次の問題を発見しました。

  • ブラウザの検出:非常にエラーが発生しやすい。デバイスにマウスまたはタッチのいずれかがあると仮定しますが、タッチディスプレイが普及すると、両方の組み合わせがますます一般的になります。
  • CSSメディア検出:私が知っている唯一のCSSのみのソリューション。それでもエラーが発生しやすく、デバイスにはマウスまたはタッチのいずれかがあり、両方とも可能であると想定されます。
  • touchendでクリックイベントをエミュレートします。ユーザーが実際にクリックする意図なしに、スクロールまたはズームのみしたい場合でも、これはリンクを誤ってたどります。リンク。
  • 変数を使用してマウスイベントを抑制します:これにより、変数がtouchendに設定され、後続のマウスイベントでif条件として使用され、状態の変化を防ぎますある時点。変数はクリックイベントでリセットされます。このページのWalter Romanの回答を参照してください。これは、タッチインターフェイスでホバー効果を本当に必要としない場合、適切なソリューションです。残念ながら、touchendが別の理由で発生し、クリックイベントが発生しない場合(たとえば、ユーザーがスクロールまたはズームした場合)は機能しません。その後、マウスでリンクをたどろうとしますインタフェース)。

参考文献

82
MacFreek

マウスを使用しているときにホバー効果を得ることができますが、タッチスクリーンを使用しているときはホバー効果を抑制できますか?

タッチスクリーンのホバー効果を抑制することではなく、マウスイベントのホバー効果を追加することではないでしょうか?

CSSに:hover効果を保持する場合は、メディアごとに異なるスタイルを指定できます。

@media screen { /* hover styles here */ } 

@media handheld { /* non-hover styles here */ }

残念ながら、これを無視して画面のルールを使用するモバイルデバイスはたくさんあります。幸いなことに、新しいモバイル/タブレットブラウザの多くは、より洗練されたメディアクエリをサポートしています。

@media screen and (max-width:800px) { /* non-hover styles here */ }

そのため、「スクリーン」または「ハンドヘルド」部分が無視された場合でも、「最大幅」はあなたのためのトリックを行います。画面が800ピクセルより小さいものはすべてタブレットまたは携帯電話である必要があり、ホバー効果を使用しないと仮定できます。低解像度のデバイスでマウスを使用しているまれなユーザーの場合、ホバー効果は表示されませんが、それ以外のサイトは問題ありません。

メディアクエリの詳細このオンラインに関する多くの記事があります-ここに1つあります: http://www.alistapart.com/articles/return-of-the-mobile-stylesheet

ホバー効果をCSSからシフトしてJavaScriptで適用すると、マウスイベントに特別にバインドできます。また、画面サイズに基づいて、最悪の場合の「問題」がいくつかあると仮定することもできます。マウスを使用しているユーザーは、ホバー効果を見逃します。

19
nnnnnn

最近のプロジェクト用に次のJSを作成しました。これは、オンタッチでは表示されないホバー効果のあるデスクトップ/モバイル/タブレットサイトです。

以下のmobileNoHoverStateモジュールには、変数preventMouseover(最初はfalseとして宣言されています)があり、ユーザーが要素でtrueイベントを起動するとtouchstartに設定されます、$target

preventMouseoverは、falseイベントが発生するたびにmouseoverに戻されます。これにより、ユーザーがタッチスクリーンとマウスの両方を使用している場合、サイトは意図したとおりに動作します。

mouseover内で宣言されている順序のため、touchstartの後にinitがトリガーされていることがわかります。

var mobileNoHoverState = function() {

    var hoverClass = 'hover',
        $target = $(".foo"), 
        preventMouseover = false;

    function forTouchstart() {
        preventMouseover = true;
    }

    function forMouseover() {
        if (preventMouseover === false) {
            $(this).addClass(hoverClass);
        } else {
            preventMouseover = false;
        }
    }

    function forMouseout() {
        $(this).removeClass(hoverClass);
    }

    function init() {
        $target.on({
            touchstart  : forTouchstart,
            mouseover   : forMouseover,
            mouseout    : forMouseout
        });                
    }

    return {
        init: init
    };
}();

次に、モジュールがさらにインスタンス化されます。

mobileNoHoverState.init();
9
Walter Roman

すべてのビューに重いjavascriptソリューションを振りかけるのは不快なオプションのように思えたので、私はこれに対して純粋なcssソリューションを本当に望んでいました。最終的に @ media.hover クエリが見つかりました。これは、「プライマリ入力メカニズムにより、ユーザーが要素にカーソルを合わせることができるかどうか」を検出できます。これにより、「ホバリング」が入力デバイスの直接的な機能よりもエミュレートされたアクションであるタッチデバイスが回避されます。

たとえば、リンクがある場合:

<a href="/" class="link">Home</a>

その後、デバイスがこのcssで簡単にサポートしている場合、:hoverのみに安全にスタイルを設定できます。

@media (hover: hover) {
  .link:hover { /* hover styles */ }
}

最新のブラウザのほとんどはインタラクションメディア機能のクエリをサポートしていますが、一部の一般的なブラウザ IEやFirefoxなど はサポートしていません。私の場合、デスクトップではChrome、モバイルではChromeおよびSafariのみをサポートすることを意図しているため、これは正常に機能します。

5
flintinatux

私の解決策は、hover-active cssクラスをHTMLタグに追加し、:hoverですべてのCSSセレクターの先頭で使用し、最初のtouchstartイベントでそのクラスを削除することです。

http://codepen.io/Bnaya/pen/EoJlb

JS:

(function () {
    'use strict';

    if (!('addEventListener' in window)) {
        return;
    }

    var htmlElement = document.querySelector('html');

    function touchStart () {
        document.querySelector('html').classList.remove('hover-active');

        htmlElement.removeEventListener('touchstart', touchStart);
    }

    htmlElement.addEventListener('touchstart', touchStart);
}());

HTML:

<html class="hover-active">

CSS:

.hover-active .mybutton:hover {
    box-shadow: 1px 1px 1px #000;
}
5
Bnaya

同じ問題を解決するために行ったのは、機能検出( このコード のようなものを使用)で、onTouchMoveが定義されているかどうかを確認し、そうであればcssクラス「touchMode」を追加することですそれ以外の場合は、「desktopMode」を追加します。

次に、何らかのスタイル効果がタッチデバイスまたはデスクトップのみに適用されるたびに、cssルールに適切なクラスが追加されます。

.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }

Edit:この戦略はもちろん、CSSにいくつかの余分な文字を追加するので、CSSサイズが気になる場合は、touchModeを検索して、 desktopModeの定義を別のファイルに配置して、デバイスタイプごとに最適化されたcssを提供できるようにします。または、prodに進む前にクラス名をもっと短い名前に変更することもできます。

2

そうです、私はjstにも同様の問題がありましたが、メディアクエリと単純なCSSでそれを修正することができました。私はここでいくつかのルールを破っていると確信していますが、それは私のために働いています。

基本的に、誰かが作った大規模なアプリケーションを用意し、それをレスポンシブにする必要がありました。彼らはjQueryUIを使用し、jQueryを改ざんしないように頼みました。そのため、CSSのみの使用に制限されていました。

タッチスクリーンモードでボタンの1つを押すと、ボタンのアクションが有効になる前にホバー効果が1秒間発生します。修正方法は次のとおりです。

@media only screen and (max-width:1024px) {

       #buttonOne{
            height: 44px;
        }


        #buttonOne:hover{
            display:none;
        }
}   
2
Daft

私のプロジェクトでは、 https://www.npmjs.com/package/postcss-hover-prefix および https://modernizr.com/ を使用してこの問題を解決しましたpostcss-hover-prefixを使用して出力後処理cssファイル。すべてのcss hoverルールに.no-touchを追加します。

const fs = require("fs");
const postcss = require("postcss");
const hoverPrfx = require("postcss-hover-prefix");

var css = fs.readFileSync(cssFileName, "utf8").toString();
postcss()
   .use(hoverPrfx("no-touch"))
   .process(css)
   .then((result) => {
      fs.writeFileSync(cssFileName, result);
   });

cSS

a.text-primary:hover {
  color: #62686d;
}

になる

.no-touch a.text-primary:hover {
  color: #62686d;
}

実行時にModernizrは次のようにhtmlタグにcssクラスを自動的に追加します

<html class="wpfe-full-height js flexbox flexboxlegacy canvas canvastext webgl 
  no-touch 
  geolocation postmessage websqldatabase indexeddb hashchange
  history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage
  borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients
  cssreflections csstransforms csstransforms3d csstransitions fontface
  generatedcontent video audio localstorage sessionstorage webworkers
  applicationcache svg inlinesvg smil svgclippaths websocketsbinary">

CssとModernizrのこのような後処理は、タッチデバイスのホバーを無効にし、他のデバイスを有効にします。実際、このアプローチはBootstrap 4に触発され、同じ問題をどのように解決したかを示しています。 https://v4-alpha.getbootstrap.com/getting-started/browsers-devices/#sticky- hoverfocus-on-mobile

2

タッチスクリーン上の要素に触れるたびに、mouseLeaveイベントをトリガーできます。すべての<a>タグのソリューションは次のとおりです。

function removeHover() {
    var anchors = document.getElementsByTagName('a');
    for(i=0; i<anchors.length; i++) {
        anchors[i].addEventListener('touchstart', function(e){
            $('a').mouseleave();
        }, false);
    }
}
1
Said Kholov

Ivはこの問題の2つの解決策を見つけました。これは、modernizrまたは他の何かでタッチを検出し、html要素にタッチクラスを設定することを意味します。

これは良いですが、サポートされているとてもよくありません:

html.touch *:hover {
    all:unset!important;
}

しかし、これは非常に優れていますsupport

html.touch *:hover {
    pointer-events: none !important;
}

私にとって完璧に動作します。ボタンに触れると、すべてのホバー効果が点灯しますが、マウスイベントの最初のホバー効果としてバグがなくなります。

非タッチデバイスからのタッチの検出は、modernizrが最高の仕事をしたと思います。

https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js

編集

この問題に対するより優れたシンプルな解決策を見つけました

クライアントがタッチデバイスかどうかを判断する方法

1
Simon Dragsbæk

ページに Modernizr を含め、代わりにホバー状態を次のように設定します。

html.no-touchevents .box:hover {
    background: blue;
}
0
Jonathan Potter

ここに私の解決策があります: http://jsfiddle.net/agamemnus/g56aw709/-- 以下のコード。

行う必要があるのは、「:hover」を「.hover」に変換することだけです...それだけです!これとその他の大きな違いは、.my_class > *:hover {などの非特異要素セレクターでも機能することです。

handle_css_hover_effects ()

function handle_css_hover_effects (init) {
 var init = init || {}
 var handle_touch_events = init.handle_touch_events || true
 var handle_mouse_events = init.handle_mouse_events || true
 var hover_class         = init.hover_class         || "hover"
 var delay_preferences   = init.delay_preferences   || {touch: {add: 500, remove: 500}}
 function default_handler (curobj, input_type, op) {
  var hovered_element_selector = "*" + ((op == "add") ? ":" : ("." + hover_class))
  var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll(hovered_element_selector))
  var modified_list = []
  while (true) {
   if ((curobj == null) || (curobj == document.documentElement)) break
   if (hovered_elements.indexOf(curobj) != -1) modified_list.Push (curobj)
   curobj = curobj.parentNode
  }
  function do_hover_change () {modified_list.forEach (function (curobj) {curobj.classList[op](hover_class)})}
  if ((!delay_preferences[input_type]) || (!delay_preferences[input_type][op])) {
   do_hover_change ()
  } else {
   setTimeout (do_hover_change, delay_preferences[input_type][op])
  }
 }

 if (handle_mouse_events) {
  document.body.addEventListener ('mouseover' , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "add")})
  document.body.addEventListener ('mouseout'  , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
  document.body.addEventListener ('click'     , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
 }

 if (handle_touch_events) {
  document.body.addEventListener ('touchstart', function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "add")})
  document.body.addEventListener ('touchend'  , function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "remove")})
  document.body.addEventListener ('touchmove',  function (evt) {
   var curobj = evt.target
   var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll("*:hover"))
   var lastobj = null
   evt = evt.changedTouches[0]
   var elements_at_point = get_elements_at_point (evt.pageX, evt.pageY)
   // Get the last element that isn't at the current point but is still hovered over, and remove only its hover attribute.
   while (true) {
    if ((curobj == null) || (curobj == document.documentElement)) break
    if ((hovered_elements.indexOf(curobj) != -1) && (elements_at_point.indexOf(curobj) == -1)) lastobj = curobj
    curobj = curobj.parentNode
   }
   if (lastobj == null) return
   if ((!delay_preferences.touch) || (!delay_preferences.touch.remove)) {
    lastobj.classList.remove(hover_class)
   } else {
    setTimeout (function () {lastobj.classList.remove(hover_class)}, delay_preferences.touch.remove)
   }

   function get_elements_at_point (x, y) {
    var el_list = [], pe_list = []
    while (true) {
     var curobj = document.elementFromPoint(x, y)
     if ((curobj == null) || (curobj == document.documentElement)) break
     el_list.Push (curobj); pe_list.Push (curobj.style.pointerEvents)
     curobj.style.pointerEvents = "none"
    }
    el_list.forEach (function (current_element, i) {current_element.style.pointerEvents = pe_list[i]})
    return el_list
   }
  })
 }
}
0
Agamemnus

CSSを見ると、奇妙な問題のように聞こえるかもしれません。しかし、とにかく、それが起こっていて他のすべてが良い場合、ホバー効果をjavascriptにシフトしてみてください(jqueryも使用できます)。単純に、mouseoverまたはより良いmouseenterイベントにバインドし、イベントが発生したときに要素を点灯します。

最後の例をここでチェックアウトしてください: http://api.jquery.com/mouseover/ 、イベントが発生したときのログに似たものを使用して、そこから取得できます!

0
balach

最近やったプロジェクトで、jQueryの委任イベント機能でこの問題を解決しました。 jQueryセレクターを使用して特定の要素を探し、マウスが要素上にあるときにCSSクラスをそれらの要素に追加/削除します。 Windows 8を実行しているタッチ対応ノートブックのIE10を含めてテストできた限り、うまく機能しているようです。

$(document).ready(
    function()
    {
        // insert your own selector here: maybe '.hoverable'?
        var selector = 'button, .hotspot';

        $('body')
            .on('mouseover', selector, function(){ $(this).addClass('mouseover');    })
            .on('mouseout',  selector, function(){ $(this).removeClass('mouseover'); })
            .on('click',     selector, function(){ $(this).removeClass('mouseover'); });
    }
);

編集:このソリューションでは、もちろん、CSSを変更して ":hover"セレクターを削除し、どの要素を使用するかを事前に検討する必要があります「ホバリング可能」である

ページ上に非常に多くの要素(数千など)がある場合、このソリューションはすべての要素ページ内で、セレクタが一致した場合にその処理を実行します。 CSSリーダーに ".hover"を書いた ":hover"を読んでほしくなかったので、CSSクラスを "hover"ではなく "mouseover"と名付けました。

0
toon81

この簡単な2019 jqueryソリューションを試してください。

  1. このプラグインをheadに追加します:

    src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"

  2. これをjsに追加します:

    $("*").on("touchend", function(e) { $(this).focus(); }); //applies to all elements

  3. これに推奨されるバリエーションは次のとおりです。

    $(":input, :checkbox,").on("touchend", function(e) {(this).focus);}); //specify elements
    
    $("*").on("click, touchend", function(e) { $(this).focus(); });  //include click event`
    
    css: body { cursor: pointer; } //touch anywhere to end a focus`
    

ノート

  • bootstrap.jsの前にプラグインを配置します(該当する場合)。ツールチップへの影響を避けるため
  • safariまたはChromeを使用して、iphone XR ios 12.1.12およびipad 3 ios 9.3.5でのみテストされています。

参照:

https://code.jquery.com/ui/

https://api.jquery.com/category/selectors/jquery-selector-extensions/

0
darren

こんにちは、未来の人、おそらくpointerhoverのメディアクエリを使用したいでしょう。 handheldメディアクエリは廃止されました。

/* device is using a mouse or similar */
@media (pointer: fine) {
  a:hover {
    background: red;
  }
}
0
olanod

JavaScriptを使用したい場合は、ページで Modernizr を使用できます。ページが読み込まれると、非タッチスクリーンブラウザーのクラス '.no-touch'がhtmlタグに追加されますが、タッチスクリーンブラウザーの場合、htmlタグにはクラス '.touch'がhtmlタグに追加されます。

次に、mouseenterリスナーとmouseleaveリスナーを追加する前に、htmlタグにno-touchクラスがあるかどうかを確認するだけです。

if($('html').hasClass('no-touch')){
    $('.box').on("mouseenter", function(event){
            $(this).css('background-color','#0000ff')
    });
    $('.box').on("mouseleave", function(event){
            $(this).css('background-color','')
    });
}

タッチスクリーンデバイスの場合、イベントにはリスナーがないため、タップしてもホバー効果はありません。

0
Stanz