私は、デスクトップとタブレットの両方から使用することを意図したWebサイトを書いています。デスクトップからアクセスしているとき、画面のクリック可能な領域を:hover
効果(異なる背景色など)で点灯させたいです。タブレットではマウスがないので、ホバーしたくないですエフェクト。
問題は、タブレット上で何かをタップすると、ブラウザには明らかに「見えないマウスカーソル」があり、それがタップした場所に移動してそこにとどまることです。他の何かをタップするまで効果をホバーします。
マウスを使用しているときにホバー効果を得ることができますが、タッチスクリーンを使用しているときはホバー効果を抑制できますか?
誰かがそれを提案することを考えていた場合、私はユーザーエージェントスニッフィングを使いたくありません。同じデバイスにタッチスクリーンとマウスの両方を搭載することもできます(今日はそれほど一般的ではないかもしれませんが、将来的にはもっと一般的です)。私はこのデバイスには興味がありません。現在どのように使用されているか、つまりマウスまたはタッチスクリーンに興味があります。
既にtouchstart
、touchmove
、および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 を投稿しました。これを使用して、タップイベントがmouseenter
、mousemove
、およびmouseleave
も起動することを確認できます。
ホバー効果によってページのコンテンツが変わるという質問からお聞きします。その場合、私のアドバイスは次のとおりです。
touchstart
およびmouseenter
にホバー効果を追加します。mouseleave
、touchmove
、およびclick
のホバー効果を削除します。または、コンテンツを変更しないようにページを編集できます。
マウスをシミュレートするために、ユーザーがタッチスクリーン(iPadなど)でタッチして指を離すと、Webkitモバイルなどのブラウザーは次のイベントを起動します(ソース: Touch And Mouse on html5rocks.com):
touchstart
touchmove
touchend
mouseover
mouseenter
mouseover
、mouseenter
、またはmousemove
イベントがページコンテンツを変更する場合、以下のイベントは発生しません。mousemove
mousedown
mouseup
click
単にマウスイベントをスキップするようにウェブブラウザに指示することは不可能と思われます。
さらに悪いことに、マウスオーバーイベントがページコンテンツを変更した場合、 Safari Webコンテンツガイド-イベントの処理 で説明されているように、クリックイベントは発生しません。特に、One -指イベント。まさに「コンテンツの変更」とは、ブラウザーとバージョンによって異なります。 iOS 7.0の場合、背景色の変更はコンテンツの変更ではない(またはもはやない)ことがわかりました。
要点をまとめると:
touchstart
およびmouseenter
にホバー効果を追加します。mouseleave
、touchmove
、およびclick
のホバー効果を削除します。touchend
にはアクションがないことに注意してください!
これは、マウスイベントに対して明らかに機能します。mouseenter
およびmouseleave
(mouseover
およびmouseout
のわずかに改善されたバージョン)が起動され、ホバーを追加および削除します。
ユーザーが実際にリンクをclick
sした場合、ホバー効果も削除されます。これにより、ユーザーがWebブラウザーの戻るボタンを押すと、確実に削除されます。
これは、タッチイベントでも機能します。タッチスタートでは、ホバー効果が追加されます。 touchendでは '' 'not' ''が削除されます。これはmouseenter
に再び追加され、これによりコンテンツの変更が発生しないため(既に追加されている)、click
イベントも発生し、ユーザーが再度クリックする必要なくリンクがたどられます!
ブラウザーがtouchstart
イベントとclick
の間に持つ300ミリ秒の遅延は、ホバー効果がこの短い時間の間に表示されるため、実際に有効に使用されます。
ユーザーがクリックをキャンセルすることを決定した場合、指を動かすと通常どおりにキャンセルされます。通常、これはmouseleave
イベントが発生しないため問題であり、ホバー効果はそのまま残ります。ありがたいことに、これはtouchmove
のホバー効果を削除することで簡単に修正できます。
それでおしまい!
たとえば、 FastClickライブラリ を使用して300ミリ秒の遅延を削除することもできますが、これはこの質問の範囲外です。
私は、次の代替案で次の問題を発見しました。
touchend
:でクリックイベントをエミュレートします。ユーザーが実際にクリックする意図なしに、スクロールまたはズームのみしたい場合でも、これはリンクを誤ってたどります。リンク。touchend
に設定され、後続のマウスイベントでif条件として使用され、状態の変化を防ぎますある時点。変数はクリックイベントでリセットされます。このページのWalter Romanの回答を参照してください。これは、タッチインターフェイスでホバー効果を本当に必要としない場合、適切なソリューションです。残念ながら、touchend
が別の理由で発生し、クリックイベントが発生しない場合(たとえば、ユーザーがスクロールまたはズームした場合)は機能しません。その後、マウスでリンクをたどろうとしますインタフェース)。mouseover
またはmousemove
イベント中にコンテンツが変更された後、それ以上イベントが発生しないことを説明しています。マウスを使用しているときにホバー効果を得ることができますが、タッチスクリーンを使用しているときはホバー効果を抑制できますか?
タッチスクリーンのホバー効果を抑制することではなく、マウスイベントのホバー効果を追加することではないでしょうか?
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で適用すると、マウスイベントに特別にバインドできます。また、画面サイズに基づいて、最悪の場合の「問題」がいくつかあると仮定することもできます。マウスを使用しているユーザーは、ホバー効果を見逃します。
最近のプロジェクト用に次の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();
すべてのビューに重いjavascriptソリューションを振りかけるのは不快なオプションのように思えたので、私はこれに対して純粋なcss
ソリューションを本当に望んでいました。最終的に @ media.hover クエリが見つかりました。これは、「プライマリ入力メカニズムにより、ユーザーが要素にカーソルを合わせることができるかどうか」を検出できます。これにより、「ホバリング」が入力デバイスの直接的な機能よりもエミュレートされたアクションであるタッチデバイスが回避されます。
たとえば、リンクがある場合:
<a href="/" class="link">Home</a>
その後、デバイスがこのcss
で簡単にサポートしている場合、:hover
のみに安全にスタイルを設定できます。
@media (hover: hover) {
.link:hover { /* hover styles */ }
}
最新のブラウザのほとんどはインタラクションメディア機能のクエリをサポートしていますが、一部の一般的なブラウザ IEやFirefoxなど はサポートしていません。私の場合、デスクトップではChrome、モバイルではChromeおよびSafariのみをサポートすることを意図しているため、これは正常に機能します。
私の解決策は、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;
}
同じ問題を解決するために行ったのは、機能検出( このコード のようなものを使用)で、onTouchMoveが定義されているかどうかを確認し、そうであればcssクラス「touchMode」を追加することですそれ以外の場合は、「desktopMode」を追加します。
次に、何らかのスタイル効果がタッチデバイスまたはデスクトップのみに適用されるたびに、cssルールに適切なクラスが追加されます。
.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }
Edit:この戦略はもちろん、CSSにいくつかの余分な文字を追加するので、CSSサイズが気になる場合は、touchModeを検索して、 desktopModeの定義を別のファイルに配置して、デバイスタイプごとに最適化されたcssを提供できるようにします。または、prodに進む前にクラス名をもっと短い名前に変更することもできます。
そうです、私はjstにも同様の問題がありましたが、メディアクエリと単純なCSSでそれを修正することができました。私はここでいくつかのルールを破っていると確信していますが、それは私のために働いています。
基本的に、誰かが作った大規模なアプリケーションを用意し、それをレスポンシブにする必要がありました。彼らはjQueryUIを使用し、jQueryを改ざんしないように頼みました。そのため、CSSのみの使用に制限されていました。
タッチスクリーンモードでボタンの1つを押すと、ボタンのアクションが有効になる前にホバー効果が1秒間発生します。修正方法は次のとおりです。
@media only screen and (max-width:1024px) {
#buttonOne{
height: 44px;
}
#buttonOne:hover{
display:none;
}
}
私のプロジェクトでは、 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
タッチスクリーン上の要素に触れるたびに、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);
}
}
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
編集
この問題に対するより優れたシンプルな解決策を見つけました
ページに Modernizr を含め、代わりにホバー状態を次のように設定します。
html.no-touchevents .box:hover {
background: blue;
}
ここに私の解決策があります: 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
}
})
}
}
CSSを見ると、奇妙な問題のように聞こえるかもしれません。しかし、とにかく、それが起こっていて他のすべてが良い場合、ホバー効果をjavascriptにシフトしてみてください(jqueryも使用できます)。単純に、mouseoverまたはより良いmouseenterイベントにバインドし、イベントが発生したときに要素を点灯します。
最後の例をここでチェックアウトしてください: http://api.jquery.com/mouseover/ 、イベントが発生したときのログに似たものを使用して、そこから取得できます!
最近やったプロジェクトで、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"と名付けました。
この簡単な2019 jqueryソリューションを試してください。
このプラグインをheadに追加します:
src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"
これをjsに追加します:
$("*").on("touchend", function(e) { $(this).focus(); }); //applies to all elements
これに推奨されるバリエーションは次のとおりです。
$(":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`
ノート
参照:
https://api.jquery.com/category/selectors/jquery-selector-extensions/
こんにちは、未来の人、おそらくpointer
やhover
のメディアクエリを使用したいでしょう。 handheld
メディアクエリは廃止されました。
/* device is using a mouse or similar */
@media (pointer: fine) {
a:hover {
background: red;
}
}
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','')
});
}
タッチスクリーンデバイスの場合、イベントにはリスナーがないため、タップしてもホバー効果はありません。