私のサイトには、_: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;背景ではありません:赤;またはテキスト装飾:下線。
「: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');
}
これは完璧に見えませんが、とにかく動作します。
:hover
はここでは問題ではありません。 iOS用Safariは非常に奇妙なルールに従います。最初にmouseover
とmousemove
を起動します。これらのイベント中に何かが変更された場合、「クリック」および関連するイベントは発生しません。
mouseenter
とmouseleave
は含まれているように見えますが、チャートには指定されていません。
これらのイベントの結果として何かを変更した場合、クリックイベントは発生しません。これには、DOMツリーの上位にあるものが含まれます。たとえば、これにより、jQueryを使用してWebサイトでシングルクリックが機能しなくなります。
$(window).on('mousemove', function() {
$('body').attr('rel', Math.random());
});
編集:説明のため、jQueryのhover
イベントにはmouseenter
とmouseleave
が含まれています。コンテンツが変更された場合、これらは両方ともclick
を防ぎます。
ブラウザ機能検出ライブラリ 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">
一部のデバイス(他の人が言っているように)には、タッチイベントとマウスイベントの両方があります。たとえば、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で修正されている場合は、テストを変更するだけです。
FastClickライブラリ をページに追加すると、モバイルデバイスのすべてのタップが(ユーザーがクリックした場所に関係なく)クリックイベントに変換されるため、モバイルデバイスのホバーの問題も修正する必要があります。例としてフィドルを編集しました: http://jsfiddle.net/FvACN/8/ 。
ページにfastclick.min.js libを含めて、次の方法でアクティブにします。
FastClick.attach(document.body);
副次的な利点として、モバイルデバイスが受ける厄介な300ミリ秒のonClick遅延も削除されます。
FastClickを使用すると、サイトにとって重要な場合も重要でない場合もありますが、いくつかの小さな影響があります。
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/
基本的に3つのシナリオがあります。
:hover
をアクティブにできます:hover
要素をアクティブにできません最初に受け入れられた答えは、ユーザーがeitherポインターまたはタッチスクリーンを持っている最初の2つのシナリオのみが可能な場合に非常に効果的です。 OPが4年前に質問したとき、これは一般的でした。一部のユーザーは、Windows 8およびSurfaceデバイスが3番目のシナリオをより可能性の高いものにしていることを指摘しています。
(@Zenexerで詳述されているように)タッチスクリーンデバイス上でホバリングできないという問題に対するiOSソリューションは賢いですが、簡単なコードの誤動作を引き起こす可能性があります(OPに記載されています)。タッチスクリーンデバイスでのみホバーを無効にするということは、タッチスクリーン対応の代替手段をコーディングする必要があることを意味します。ユーザーがポインターとタッチスクリーンの両方を持っていることを検出すると、さらに水が濁ります(@Simon_Weaverによる説明)。
この時点で、最も安全な解決策は、ユーザーがWebサイトと対話できる唯一の方法として:hover
を使用しないことです。ホバー効果は、リンクまたはボタンがアクション可能であることを示す良い方法ですが、ユーザーがWebサイトでアクションを実行するために要素をホバーする必要はありません。
タッチスクリーンを念頭に置いて「ホバー」機能を再考する は、代替のUXアプローチについて適切に議論しています。そこでの答えによって提供されるソリューションは次のとおりです。
今後、これはおそらくすべての新しいプロジェクトにとって最適なソリューションになるでしょう。受け入れられた答えはおそらく2番目に良い解決策ですが、ポインタデバイスも持っているデバイスを考慮してください。 iOSの:hover
ハックを回避するためだけにデバイスにタッチスクリーンがある場合、機能を削除しないように注意してください。
.cssのJQueryバージョンは、すべてのホバールールに.no-touch .my-element:hoverを使用します。これには、JQueryと次のスクリプトが含まれます
function removeHoverState(){
$("body").removeClass("no-touch");
}
次に、bodyタグにclass = "no-touch" ontouchstart = "removeHoverState()"を追加します
ontouchstartが起動すると、すべてのホバー状態のクラスが削除されます
答えてくれた@Morgan Chengに感謝しますが、「touchstart」(@ Timothy Perezから取られたコード- answer )ただし、これにはjQuery 1.7以降が必要です
$(document).on({ 'touchstart' : function(){
//do whatever you want here
} });
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
}
});
このメソッドは、最初にマウスオーバーを起動し、次にクリックします。
私は、タッチのホバーを無効にすることが道であることに同意します。
ただし、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)" />
タッチが利用できないときにホバー効果を持たせる代わりに、タッチイベントを処理するシステムを作成しました。これにより、問題が解決しました。最初に、「タップ」(「クリック」に相当)イベントをテストするためのオブジェクトを定義しました。
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.
});