私はさまざまな機器で動かなければならないモバイルWebサイトに取り組んでいます。現時点で私に頭痛を与えているのはBlackBerryです。
キーボードのクリックとタッチイベントの両方をサポートする必要があります。
理想的には私が使用するだけです:
$thing.click(function(){...})
しかし、私たちが遭遇している問題は、これらのブラックベリーデバイスの中には、タッチの時からクリックを引き起こすまでの非常に厄介な遅れがあることです。
解決策は、代わりにtouchstartを使用することです。
$thing.bind('touchstart', function(event){...})
しかし、どのようにして両方のイベントをバインドし、片方だけを起動するのでしょうか。キーボードデバイスにはクリックイベントがまだ必要ですが、もちろん、タッチデバイスを使用している場合はクリックイベントを発生させたくありません。
ボーナス質問:これをして、さらにタッチスタートイベントさえ持っていないブラウザに対応するためにとにかくありますか?これを調べてみると、BlackBerry OS 5はtouchstartをサポートしていないように見えるので、そのブラウザのクリックイベントにも依存する必要があります。
補遺:
おそらく、より包括的な質問は:
JQueryでは、タッチ操作とマウス操作の両方を同じバインディングで処理することは可能ですか/推奨されますか?
理想的には、答えはイエスです。そうでなければ、私はいくつかのオプションがあります:
1)デバイス情報を取得するためにWURFLを使用するため、独自のデバイスマトリックスを作成できます。端末によっては、touchstart OR clickを使用します。
2)JSを介してブラウザのタッチサポートを検出します(それについてはもう少し調査する必要がありますが、実行可能なようです)。
ただし、それでも1つの問題が残ります。両方をサポートするデバイスについてはどうでしょうか。私たちがサポートする携帯電話の中には(つまりNokiasとBlackBerries)両方のタッチスクリーンとのキーボードを持っているものがあります。それで、そのようなことは私を完全に元の質問に連れ戻します...どういうわけか両方を同時に考慮に入れる方法はありますか?
更新:jQuery Pointer Events Polyfill プロジェクトをチェックしてください。これにより、 "pointer"イベントにバインドできます。マウスとタッチのどちらかを選ぶ代わりに。
両方にバインドします。ただし、関数が100ミリ秒に1回だけ起動するようにフラグを立てます。
var flag = false;
$thing.bind('touchstart click', function(){
if (!flag) {
flag = true;
setTimeout(function(){ flag = false; }, 100);
// do something
}
return false
});
これは私が "作成"した修正であり、GhostClickを取り出してFastClickを実装しています。あなた自身で試して、それがあなたのために働いたかどうか私たちに知らせてください。
$(document).on('touchstart click', '.myBtn', function(event){
if(event.handled === false) return
event.stopPropagation();
event.preventDefault();
event.handled = true;
// Do your magic here
});
あなたはこのようなことを試すことができます:
var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
通常これも同様に機能します。
$('#buttonId').on('touchstart click', function(e){
e.stopPropagation(); e.preventDefault();
//your code here
});
on("click touchstart")
イベント関数の最後にreturn false;
を追加するだけでこの問題を解決できます。
$(this).on("click touchstart", function() {
// Do things
return false;
});
。on() に関するjQueryのドキュメントから
イベントハンドラから
false
を返すと、自動的にevent.stopPropagation()
とevent.preventDefault()
が呼び出されます。false
値はfunction(){ return false; }
の省略形としてハンドラに渡すこともできます。
私は似たようなことをしなければなりませんでした。これは私のために働いたものの簡略版です。タッチイベントが検出された場合は、クリックバインディングを削除します。
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click');
//your code here
});
私の場合はclickイベントが<a>
要素にバインドされていたので、クリックバインディングを削除し、<a>
要素に対するデフォルトのアクションを妨げるクリックイベントを再バインドしなければなりませんでした。
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click').on('click', function(e){ e.preventDefault(); });
//your code here
});
私は次のように成功しました。
イージーピージー...
$(this).on('touchstart click', function(e){
e.preventDefault();
//do your stuff here
});
通常、デフォルトのタッチと非タッチ(クリック)APIを混在させたくありません。いったんあなたがタッチの世界に入ると、それはタッチ関連の機能だけを扱うことがより簡単になります。以下はあなたが望むことをするであろういくつかの疑似コードです。
Touchmoveイベントに接続して場所を追跡する場合は、doTouchLogic関数に項目を追加してジェスチャやその他の機能を検出できます。
var touchStartTime;
var touchStartLocation;
var touchEndTime;
var touchEndLocation;
$thing.bind('touchstart'), function() {
var d = new Date();
touchStartTime = d.getTime();
touchStartLocation = mouse.location(x,y);
});
$thing.bind('touchend'), function() {
var d = new Date();
touchEndTime= d.getTime();
touchEndLocation= mouse.location(x,y);
doTouchLogic();
});
function doTouchLogic() {
var distance = touchEndLocation - touchStartLocation;
var duration = touchEndTime - touchStartTime;
if (duration <= 100ms && distance <= 10px) {
// Person tapped their finger (do click/tap stuff here)
}
if (duration > 100ms && distance <= 10px) {
// Person pressed their finger (not a quick tap)
}
if (duration <= 100ms && distance > 10px) {
// Person flicked their finger
}
if (duration > 100ms && distance > 10px) {
// Person dragged their finger
}
}
私はベストプラクティスは今使うことだと思います:
$('#object').on('touchend mouseup', function () { });
touchend
タッチポイントからタッチポイントが削除されると、タッチイベントが発生します。
Touchendイベントはマウスイベントを発生させません。
マウスアップ
マウスポインタが要素上にあるときにmouseupイベントが要素に送信され、マウスボタンが離されます。どのHTML要素でもこのイベントを受け取ることができます。
Mouseupイベントはいかなるタッチイベントも引き起こさないことはありません。
EXAMPLE
$('#click').on('mouseup', function () { alert('Event detected'); });
$('#touch').on('touchend', function () { alert('Event detected'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="click">Click me</h1>
<h1 id="touch">Touch me</h1>
EDIT(2017)
2017年の時点で、ブラウザはChromeから始まり、クリックリクエストのタップイベントによって発生する遅延を排除することでクリックイベント.on("click")
をマウスとタッチの両方に対してより互換性のあるものにするためのステップを作りました。
これは、クリックイベントだけを使用することに戻ることが最も簡単な解決策であるという結論に至ります。
これが実際的かどうかを確認するためのクロスブラウザテストはまだ行っていません。
ええと、これらはすべて非常に複雑です。
Modernizrがあれば、それは非常に簡単です。
ev = Modernizr.touch ? 'touchstart' : 'click';
$('#menu').on(ev, '[href="#open-menu"]', function(){
//winning
});
高速ボタンをチェックし、Googleからクリックを選択する https://developers.google.com/mobile/articles/fast_buttons
より良いメンテナンスのためのもう一つの実装。しかしながら、このテクニックはevent.stopPropagation()も行います。クリックは100msクリックした他のどの要素にもキャッチされません。
var clickObject = {
flag: false,
isAlreadyClicked: function () {
var wasClicked = clickObject.flag;
clickObject.flag = true;
setTimeout(function () { clickObject.flag = false; }, 100);
return wasClicked;
}
};
$("#myButton").bind("click touchstart", function (event) {
if (!clickObject.isAlreadyClicked()) {
...
}
}
ontouchstart
が引き起こされたことがあるかどうかを暗記するというアイデアを思いついたのです。この場合、それをサポートしているデバイスを使用しており、onclick
イベントを無視したいです。 ontouchstart
はいつもの前にonclick
をトリガーする必要があるので、私はこれを使っています:
<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>
あなたはこのように試すことができます:
var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);
ドキュメンテーションの目的のためだけに、ここで私が考えることができるデスクトップ上の最速/最も反応の速いクリック/モバイルソリューションのタップのために私がしたことがあります:
私はjQueryのon
関数を、ブラウザがタッチイベントをサポートしているときはいつでも、私のすべてのクリックイベントをtouchstartに置き換えた修正されたものに置き換えました。
$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
on: (function(){
var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
return function( types, selector, data, fn, one ) {
if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
return this._on( types, selector, data, fn);
};
}()),
});
使い方は以前とまったく同じですが、
$('#my-button').on('click', function(){ /* ... */ });
ただし、利用可能な場合はtouchstartを使用し、利用できない場合はclickを使用します。いかなる遅延も必要ありません:D
これは私のために働いた、モバイルは両方を聴くので、タッチイベントであるものを防ぐ。デスクトップはマウスだけを聴きます。
$btnUp.bind('touchstart mousedown',function(e){
e.preventDefault();
if (e.type === 'touchstart') {
return;
}
var val = _step( _options.arrowStep );
_evt('Button', [val, true]);
});
私の場合、これは完璧に機能しました。
jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
jQuery(this).off('mouseup');
}
});
主な問題は、代わりにクリックしてマウスをクリックしたときに、タッチデバイスでクリックとタッチエンドが同時に発生し、クリックオフを使用した場合、一部の機能がモバイルデバイスでまったく機能しないことでした。クリックに関する問題はそれがtouchendを含むイベントの残りの部分を起動するグローバルなイベントであるということです。
私にとっては、Mottieが提供したものに最もよく答えるために、私は彼のコードをもっと再利用可能にしようとしているので、これが私の貢献です。
bindBtn ("#loginbutton",loginAction);
function bindBtn(element,action){
var flag = false;
$(element).bind('touchstart click', function(e) {
e.preventDefault();
if (!flag) {
flag = true;
setTimeout(function() {
flag = false;
}, 100);
// do something
action();
}
return false;
});
私はこれを試していて、これまでのところそれは動作します(しかし私はAndroid/PhoneGapの上にいるのでとても注意してくださいemptor)
function filterEvent( ob, ev ) {
if (ev.type == "touchstart") {
ob.off('click').on('click', function(e){ e.preventDefault(); });
}
}
$('#keypad').on('touchstart click', '.number, .dot', function(event) {
filterEvent( $('#keypad'), event );
console.log( event.type ); // debugging only
... finish handling touch events...
}
私はすべてのタッチでハンドラーを再バインドしているという事実は好きではありませんが、タッチと見なされるすべてのことがあまり頻繁には発生しません(コンピューター時間内)。
私は '#keypad'のためのもののようなたくさんのハンドラを持っているので、あまりにも多くのコードなしで問題に対処することを可能にする単純な関数を持つことが私がこのように行った理由です。
jQuery Mobile
からVirtual Mouse (vmouse) Bindings
を使用してみてください。それはあなたの場合は特に仮想的なイベントです。
$thing.on('vclick', function(event){ ... });
http://api.jquerymobile.com/vclick/
ブラウザサポートリスト: http://jquerymobile.com/browser-support/1.4/
私はそこに答え をしました そして私は jsfiddle でデモンストレーションします。あなたは別のデバイスをチェックしてそれを報告することができます。
基本的に私はそれを提供するいくつかの関数で一種のイベントロックを使用します。
/*
* Event lock functions
* ====================
*/
function getEventLock(evt, key){
if(typeof(eventLock[key]) == 'undefined'){
eventLock[key] = {};
eventLock[key].primary = evt.type;
return true;
}
if(evt.type == eventLock[key].primary)
return true;
else
return false;
}
function primaryEventLock(evt, key){
eventLock[key].primary = evt.type;
}
それから、私のイベントハンドラで私は私のロックへのリクエストから始めます:
/*
* Event handlers
* ==============
*/
$("#add").on("touchstart mousedown", addStart);
$("#add").on("touchend mouseup", addEnd);
function addStart(evt){
// race condition between 'mousedown' and 'touchstart'
if(!getEventLock(evt, 'add'))
return;
// some logic
now = new Date().getTime();
press = -defaults.pressDelay;
task();
// enable event lock and(?) event repetition
pids.add = setTimeout(closure, defaults.pressDelay);
function closure(){
// some logic(?): comment out to disable repetition
task();
// set primary input device
primaryEventLock(evt, 'add');
// enable event repetition
pids.add = setTimeout(closure, defaults.pressDelay);
}
}
function addEnd(evt){
clearTimeout(pids.add);
}
私は、問題は単にイベントで応答するのではなく、NOTで応答することであることを強調しなければなりません。
最後に、jsfiddleでは、イベントの開始と終了の両方のハンドラでイベントロックライブラリへの単純な呼び出しを追加することによって、既存のコードに最小限の影響しか与えない更新バージョンへのリンクがあります2つのスコープ変数eventLock
とeventLockDelay
。
私が見つけた最良の方法は、touchイベントを書き、そのイベントが通常のclickイベントをプログラム的に呼び出すようにすることです。これにより、通常のクリックイベントがすべて発生し、すべてのタッチイベントに対して1つのイベントハンドラを追加するだけで済みます。タッチ可能にしたいすべてのノードに対して、タッチハンドラを呼び出すために「touchable」クラスを追加するだけです。 Jqueryでは、論理的には本当のタッチイベントであり、誤検出ではないことを確認するために、いくつかのロジックを使用してそのように動作します。
$("body").on("touchstart", ".touchable", function() { //make touchable items fire like a click event
var d1 = new Date();
var n1 = d1.getTime();
setTimeout(function() {
$(".touchable").on("touchend", function(event) {
var d2 = new Date();
var n2 = d2.getTime();
if (n2 - n1 <= 300) {
$(event.target).trigger("click"); //dont do the action here just call real click handler
}
});
}, 50)}).on("click", "#myelement", function() {
//all the behavior i originally wanted
});
編集:私の以前の回答(このスレッドの回答に基づく)は私のために進むことができませんでした。私はサブメニューをマウスの入力やタッチクリックで展開し、マウスの離脱や別のタッチクリックで折りたたむことを望んでいました。マウスイベントは通常タッチイベントの後に発生するので、タッチスクリーンとマウス入力の両方を同時にサポートするイベントリスナーを書くのはちょっとトリッキーでした。
イベントがタッチスクリーンとマウスのどちらによって呼び出されたのかを検出できる " Touch Or Mouse "(897バイト縮小)というjQueryプラグインを書きました。タッチサポートのためのテストなしで!)これは同時にタッチスクリーンとマウスの両方のサポートを可能にしますそしてそれらのイベントを完全に分離します。
このようにして、OPはタッチクリックにすばやく反応させるためにtouchstart
またはtouchend
を使用し、マウスでonlyをクリックされたときにclick
を使用できます。
最初に1つ作る必要があります。 body要素はタッチイベントを追跡します。
$(document.body).touchOrMouse('init');
マウスイベントはデフォルトの方法で要素にバインドされており、$body.touchOrMouse('get', e)
を呼び出すことで、イベントがタッチスクリーンまたはマウスのどちらによって呼び出されたのかを知ることができます。
$('.link').click(function(e) {
var touchOrMouse = $(document.body).touchOrMouse('get', e);
if (touchOrMouse === 'touch') {
// Handle touch click.
}
else if (touchOrMouse === 'mouse') {
// Handle mouse click.
}
}
http://jsfiddle.net/lmeurs/uo4069nh で仕事中のプラグインを参照してください。
touchstart
イベントとtouchend
イベントを追跡するためのbody要素。このようにして、touchend
イベントをトリガー要素(つまりリンクやボタン)で発生させる必要はありません。この2つのタッチイベントの間、このプラグインはマウスイベントがタッチによって呼び出されたと見なします。touchend
の後にghostEventDelay
(オプション、デフォルトでは1000ms)以内に発生する場合、このプラグインはtouchend
の後にのみ発生します。このプラグインはマウスイベントがtouchによって発生するとみなします。:active
状態になります。 mouseleave
イベントは、要素がieによってこの状態を失った後にのみ発生します。他の要素をクリックする。これはmouseenter
イベントが発生してから数秒(または数分!)かかる可能性があるため、このプラグインは要素の最後のmouseenter
イベントを追跡します。最後のmouseenter
イベントがtouchによって呼び出された場合、次のmouseleave
イベントも次のイベントによって呼び出されます。タッチ。簡単な方法は次のとおりです。
// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) {
// Do your stuff here
}
});
基本的には、ルートドキュメントに添付されているjQueryのデータオブジェクトの 'trigger'プロパティにトリガーされる最初のイベントタイプを保存し、イベントタイプが 'trigger'の値と等しい場合にのみ実行します。タッチデバイスでは、イベントチェーンは「touchstart」の後に「click」が続く可能性があります。しかし、 "click"は "trigger"( "touchstart")に保存されている最初のイベントタイプと一致しないため、 'click'ハンドラは実行されません。
仮定は、そしてそれが安全なものだと私は信じています、あなたのスマートフォンはタッチデバイスからマウスデバイスに自然に変わらないか、そうでなければ決してタップは登録されないでしょう。ページの読み込みと「クリック」が「touchstart」と一致することはありません。
これは、遊ぶことができるcodepenです(タッチデバイスのボタンをタップしてみてください - クリック遅延はありません): http://codepen.io/thdoan/pen/xVVrOZ
私はまたこれをセレクタ文字列を渡すことによってjQueryの子孫フィルタリングをサポートする簡単なjQueryプラグインとして実装しました:
// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
var selector, handler;
switch (typeof arg1) {
case 'function':
selector = null;
handler = arg1;
break;
case 'string':
selector = arg1;
if (typeof arg2==='function') handler = arg2;
else return;
break;
default:
return;
}
this.on('click touchstart', selector, function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
});
};
Codepen: http://codepen.io/thdoan/pen/GZrBdo/
文書のスクロール移動の違い(水平方向と垂直方向の両方)のタッチスタートとタッチエンドを見つけ、そのうちの1つが1ピクセルより大きい場合は、クリックではなく移動です。
var touchstartverscrollpos , touchstarthorscrollpos;
$('body').on('touchstart','.thumbnail',function(e){
touchstartverscrollpos = $(document).scrollTop();
touchstarthorscrollpos = $(document).scrollLeft();
});
$('body').on('touchend','.thumbnail',function(e){
var touchendverscrollpos = $(document).scrollTop();
var touchendhorscrollpos = $(document).scrollLeft();
var verdiff = touchendverscrollpos - touchstartverscrollpos;
var hordiff = touchendhorscrollpos - touchstarthorscrollpos;
if (Math.abs(verdiff) <1 && Math.abs(hordiff)<1){
// do you own function () here
e.stopImmediatePropagation();
return false;
}
});
これはここで言及されていませんが、あなたはこのリンクをチェックアウトしたいかもしれません: https://joshtronic.com/2015/04/19/handling-click-and-touch-events同じ要素上の/
両方のハンドラに割り当てて結果をソートするのではなく、後世に遡るために、単にデバイスがタッチスクリーンであるかどうかを確認して、関連するイベントのみに割り当てることができます。観察する:
var clickEvent = (function() {
if ('ontouchstart' in document.documentElement === true)
return 'touchstart';
else
return 'click';
})();
// and assign thusly:
el.addEventListener( clickEvent, function( e ){
// things and stuff
});
私はこれを使って自分のイベントをバインドし、2回発生するtouchstart
とclick
の両方のイベントを扱うタッチスクリーンと、click
しか聞こえない私の開発PCでテストできるようにします。
このリンクの作者が言及している1つの問題は、両方のイベントを処理するように設計されたタッチスクリーンラップトップです。
私が考えていなかった3つ目のデバイス、タッチスクリーンラップトップについて学びました。タッチイベントとクリックイベントの両方をサポートするハイブリッドデバイスです。 1つのイベントをバインドすると、そのイベントのみがサポートされます。それは私が扱っている唯一のイベントだからです。
touchstart
とclick
をバインドすることは、これらのハイブリッドデバイスを処理するのに理想的なようです。イベントが2回発生しないようにするために、コールバック関数にe.stopPropagation()
とe.preventDefault()
を追加しました。e.stopPropagation()
は、イベントが両親に「バブリング」するのを防ぎますが、2番目のイベントの発生も防ぎます。 「念のため」としてe.preventDefault()
を含めましたが、省略することも可能です。
私はまたAndroid/iPadウェブアプリに取り組んでいます、そして、それは「touchmove」を使うことだけが「コンポーネントを動かす」のに十分であるならば(タッチスタートは必要ありません)。 touchstartを無効にすると、.click()を使用できます。 jQueryから。それはtouchstartによって過負荷になっていないのでそれは実際に働いています。
最後に、.live( "touchstart"、function(e){e.stopPropagation();});をバインドできます。 touchstartイベントに伝播を停止するように依頼するには、リビングルームをclick()にトリガーします。
それは私のために働きました。
この問題を解決しようとするときに考慮すべきことがたくさんあります。ほとんどの解決策は、スクロールを中断するか、ゴーストクリックイベントを適切に処理しません。
完全な解決策については、 https://developers.google.com/mobile/articles/fast_buttons をご覧ください。
注意:ゴーストクリックイベントを要素ごとに処理することはできません。遅延クリックは画面の位置によって発生するため、タッチイベントが何らかの方法でページを変更した場合、クリックイベントは新しいバージョンのページに送信されます。
click
を使用することによる望ましくない副作用を避けるために、イベント'touchstart mousedown'
または'touchend mouseup'
に割り当てることが効果的かもしれません。
なぜjQuery Event APIを使わないのですか?
http://learn.jquery.com/events/event-extensions/
私はこの単純なイベントを成功裏に使用しました。それは、クリーンで、名前空間があり、改良するのに十分なほど柔軟です。
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var eventType = isMobile ? "touchstart" : "click";
jQuery.event.special.touchclick = {
bindType: eventType,
delegateType: eventType
};
あなたがjQueryを使っているなら、以下は私にとってはうまくいった:
var callback; // Initialize this to the function which needs to be called
$(target).on("click touchstart", selector, (function (func){
var timer = 0;
return function(e){
if ($.now() - timer < 500) return false;
timer = $.now();
func(e);
}
})(callback));
他の解決策も良いですが、私はループ内で複数のイベントを束縛していたので、適切なクロージャを作成するために自己呼び出し関数が必要でした。また、バインディングを無効にしたくはありませんでした。次回のクリックやタッチスタートで起動できるようにしたいからです。
同様の状況で誰かを助けるかもしれません!
簡単な機能のためには、単にタッチを認識するかクリックするだけです。私は以下のコードを使います:
var element = $("#element");
element.click(function(e)
{
if(e.target.ontouchstart !== undefined)
{
console.log( "touch" );
return;
}
console.log( "no touch" );
});
これは、touchstartイベントが定義されている場合は "touch"を、定義されていない場合は "no touch"を返します。私が言ったように、これはクリック/タップイベントに対する単純なアプローチです。
UDPATE:
私は同じイベントに対してclickイベントとtouchendイベントの両方を使用するための実装に取り組んできました。そして、型が変更された場合、関数はイベントを効果的にブロックします。私の目標は、より応答性の高いアプリケーションインターフェースを持つことでした - 私は、イベントがUIフィードバックループになるまでの時間を短縮したいと思いました。
この実装が機能するためには、「クリック」と「touchend」に関連イベントがすべて追加されていることが前提です。これにより、両方のイベントの実行が要求されてもタイプが異なる場合に、1つの要素からイベントのバブリングが奪われることを防ぎます。
これは、デモ目的で単純化した軽量のAPIベースの実装です。折りたたみ要素の機能を使用する方法を示します。
var tv = {
/**
* @method eventValidator()
* @desc responsible for validating event of the same type.
* @param {Object} e - event object
* @param {Object} element - element event cache
* @param {Function} callback - callback to invoke for events of the same type Origin
* @param {Object} [context] - context to pass to callback function
* @param {Array} [args] - arguments array to pass in with context. Requires context to be passed
* @return {Object} - new event cache
*/
eventValidator: function(e, element, callback, context, args){
if(element && element.type && element.type !== e.type){
e.stopPropagation();
e.preventDefault();
return tv.createEventCacheObj({}, true);
} else {
element = tv.createEventCacheObj(e);
(typeof context === "object" ? callback.apply(context, args) : callback());
return element;
}
},
/**
* @method createEventCacheObj()
* @param {Object} event - event object
* @param {String} [event.type] - event type
* @param {Number} [event.timeStamp] - time of event in MS since load
* @param {Boolean} [reset=false] - flag to reset the object
* @returns {{type: *, time: string}}
*/
createEventCacheObj: function (event, reset){
if(typeof reset !== 'boolean') reset = false;
return {
type: !reset ? event.type : null,
time: !reset ? (event.timeStamp).toFixed(2): null
};
}
};
// Here is where the magic happens
var eventCache = [];
var pos = 0;
var $collapses = document.getElementsByClassName('tv-collapse__heading');
Array.prototype.forEach.call($collapses, function(ele){
ele.addEventListener('click', toggleCollapse);
ele.addEventListener('touchend', toggleCollapse);
// Cache mechanism
ele.setAttribute('data-event-cache', String(pos++));
});
/**
* @func toggleCollapse()
* @param {Object} e - event object
* @desc responsible for toggling the state of a collapse element
*/
function toggleCollapse(e){
eventCache[pos] = tv.eventValidator(e, eventCache[pos], function(){
// Any event which isn't blocked will run the callback and its content
// the context and arguments of the anonymous function match the event function context and arguments (assuming they are passed using the last two parameters of tv.eventValidator)
}, this, arguments);
}
元の答え:
これが Rafael Fragosoの 答え - 純粋なJSの修正である応答です。
(function(){
button = document.getElementById('sayHi');
button.addEventListener('touchstart', ohHai);
button.addEventListener('click', ohHai);
function ohHai(event){
event.stopPropagation();
event.preventDefault();
console.log('ohHai is:', event.type);
};
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SO - Answer</title>
</head>
<body>
<button id="sayHi">Anyone there?</button>
</body>
</html>
次のコードを実行して、出力に注意してください。
重要なのは、連続したイベントが発生しないようにすることです。モバイルブラウザは、タッチが発生したときにクリックをエミュレートするために最善を尽くします。クリックしてタッチスタートした後に発生するすべてのイベントについて説明した、しばらく前に見た記事へのリンクを見つけられたらと思います。 (私はダブルタップとクリックが実際に発砲するまでの300msの遅延を探していました)。
タッチデバイスとマウスデバイス
私はSurface Proとタッチスクリーン付きのwindows 10デスクトップを使っていくつかのテストを実行しました。私が発見したのは、どちらもあなたが疑っているようにイベントを引き起こし、タッチでタッチスタートし、トラックパッド、マウス、そしてスタイリストでクリックしたということです。おもしろいことに、ボタン上ではなく近くにあったタッチイベントが、タッチイベントなしでクリックイベントを引き起こすということでした。 Windows 10の組み込み機能は半径内で最も近いノードを探し、ノードが見つかるとマウスベースのイベントを発生させます。
同じタイプの複数のイベント
同じタイプの2つのイベントが要素上にある場合は、イベントの発生を止めると、一方のイベントが発生しなくなる可能性があります。ある種のキャッシュを使用してこれを処理する方法はいくつかあります。私の最初の考えはイベントオブジェクトを修正することでしたが、私達は参照を得ますので私はキャッシュソリューションで十分でなければならないと思います。
クリックがタッチイベントに続くalwaysという事実を利用して、タイムアウトやグローバルフラグを使用せずに「ゴーストクリック」を回避するために行ったことがここにあります。
$('#buttonId').on('touchstart click', function(event){
if ($(this).data("already")) {
$(this).data("already", false);
return false;
} else if (event.type == "touchstart") {
$(this).data("already", true);
}
//your code here
});
基本的に、ontouchstart
イベントが要素に対して発生するたびに、フラグが設定され、その後クリックが来たときに削除されます(そして無視されます)。