DOM要素でclickイベントとdblclickイベントの両方を処理しています。それぞれが異なるコマンドを実行しますが、要素をダブルクリックすると、ダブルクリックイベントが発生するだけでなく、クリックイベントも2回発生することがわかりました。この動作を防ぐための最良のアプローチは何ですか?
13 に答える
他の誰かが(私がしたように)これにつまずいて答えを探している場合、私が思いつくことができる絶対的な最善の解決策は次のとおりです。
$node.on('click',function(e){
if(e.originalEvent.detail > 1){
return;
/* if you are returning a value from this
function then return false or cancel
the event some other way */
}
});
終わり。背中合わせに複数回クリックする場合は、2回目、3回目など。発砲しません。私は間違いなくこれをどんな種類のタイマーを使うよりも好みます。
これを読んで、私は自分自身をこの方向に向けさせました。
ちなみに、私が最初にこの問題を調査したのは、ページ分割されたリンクを誤ってダブルクリックし、コールバックが発生する前にイベントが発生して2回終了したためです。
上記のコードを思い付く前に、私は持っていました
if e.originalEvent.detail === 2 //return
ただし、リンクを3回クリック(トリプルクリック)することができ、2回目のクリックは発生しませんでしたが、3回目のクリックは発生しました。
コメントで、あなたは言った、
クリックハンドラーを300ミリ秒(目立って迷惑な遅延)、さらには...
つまり、クリックすると、クリックがダブルクリックの最初のクリックである場合を除いて、DOMはクリックイベントをすぐに生成する必要があるように思えます。
この機能を実装するには、クリックしたときに、これが最後のクリックなのか、それともダブルクリックの最初のクリックなのかをDOMが予測できる必要があります(ただし、一般的にDOMが予測することは不可能だと思います)。ユーザーがもう一度クリックしようとしているかどうか)。
クリックとダブルクリックで実行しようとしている2つの異なるアクションは何ですか?IMO、通常のアプリケーションでは、両方のイベントが必要になる場合があります。たとえば、シングルクリックして要素にフォーカスし、ダブルクリックしてアクティブにします。
イベントを分離する必要がある場合、一部のアプリケーションはダブルクリック以外のものを使用します。たとえば、右クリックやコントロールクリックを使用します。
UIEvent.detail
要素がクリックされた回数を検出し、それに基づいてイベントを発生させる場合に使用できます。
簡単な例:
element.addEventListener("click", function (e) {
if (e.detail === 1) {
// do something if the element was clicked once.
} else if (e.detail === 2) {
// do something else if the element was clicked twice
}
});
この場合、シングルクリックイベントの実行を少し遅らせるのが最善です。ダブルクリックハンドラーに、シングルクリックイベントがチェックする変数を設定してもらいます。その変数に特定の値がある場合は、である可能性があります。シングルクリックboolDoubleClick == true
しないでください。fire/handle
相互作用が両方を必要とするが、相互に排他的である場合、それらの組み合わせが私に合理的な解決策を提供するように思われるので、ここで他のすべての答えに感謝します:
var pendingClick = 0;
function xorClick(e) {
// kill any pending single clicks
if (pendingClick) {
clearTimeout(pendingClick);
pendingClick = 0;
}
switch (e.detail) {
case 1:
pendingClick = setTimeout(function() {
console.log('single click action here');
}, 500);// should match OS multi-click speed
break;
case 2:
console.log('double click action here');
break;
default:
console.log('higher multi-click actions can be added as needed');
break;
}
}
myElem.addEventListener('click', xorClick, false);
更新:このアプローチの一般化されたバージョンと、タッチデバイス用のクリックポリフィルをこのGithubリポジトリに追加しました。例を示します。
AFAIK DOMレベル2イベントでは、ダブルクリックの指定はありません。IE7では動作しません(ショックがあります)が、FFとOperaは仕様の管理に問題はなく、すべてのアクションをクリックイベントに添付できますが、ダブルクリックの場合は「詳細」属性まで待つだけですイベントオブジェクトの値は2です。ドキュメントから:「同じ画面位置で複数のクリックが発生した場合、シーケンスは繰り返され、繰り返しごとにdetail属性が増加します。」
これが私がモジュール内で区別するためにしたことです
node.on('click', function(e) {
//Prepare for double click, continue to clickHandler doesn't come soon enough
console.log("cleared timeout in click",_this.clickTimeout);
clearTimeout(_this.clickTimeout);
_this.clickTimeout = setTimeout(function(){
console.log("handling click");
_this.onClick(e);
},200);
console.log(_this.clickTimeout);
});
node.on('dblclick', function (e) {
console.log("cleared timeout in dblclick",_this.clickTimeout);
clearTimeout(_this.clickTimeout);
// Rest of the handler function
別のことを行う必要があるdblclickイベントがあった場合、クリックイベントアクションを防ぐためにプロジェクトにこのソリューションを使用します。
注:このソリューションは、クリックとdblclick専用であり、トリプルクリックなどの他のものではありません。
クリックとダブルクリックの間の適切な時間を確認するには、こちらをご覧ください
英語が下手でごめんなさい。お役に立てば幸いです:)
var button, isDblclick, timeoutTiming;
var clickTimeout, dblclickTimeout;
//-----
button = $('#button');
isDblclick = false;
/*
the proper time between click and dblclick is not standardized,
and is cutsomizable by user apparently (but this is windows standard I guess!)
*/
timeoutTiming = 500;
//-----
button.on('dblclick', function () {
isDblclick = true;
clearTimeout(dblclickTimeout);
dblclickTimeout = setTimeout(function () {
isDblclick = false;
}, timeoutTiming);
//-----
// here goes your dblclick codes
console.log('double clicked! not click.');
}).on('click', function () {
clearTimeout(clickTimeout);
clickTimeout = setTimeout(function () {
if(!isDblclick) {
// here goes your click codes
console.log('a simple click.');
}
}, timeoutTiming);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" id="button">
click/dblclick on this to see the result
</button>
これはかなり古いことは知っていますが、同じ問題が発生したばかりなので、とにかく投稿したいと思いました。これが私がそれを解決した方法です。
$('#alerts-display, #object-display').on('click', ['.item-data-summary', '.item-marker'], function(e) {
e.preventDefault();
var id;
id = setTimeout(() => {
// code to run here
return false;
}, 150);
timeoutIDForDoubleClick.push(id);
});
$('.panel-items-set-marker-view').on('dblclick', ['.summary', '.marker'], function(e) {
for (let i = 0; i < timeoutIDForDoubleClick.length; i++) {
clearTimeout(timeoutIDForDoubleClick[i]);
}
// code to run on double click
e.preventDefault();
});
これが2回目のクリックを防ぐための私の簡単な解決策です。もちろん、ダブルクリックが検出されたときにタイムアウトを再開することはできますが、実際にはそれは必要ありません。
clickTimeoutId = null;
onClick(e) {
if (clickTimeoutId !== null) {
// Double click, do nothing
return;
}
// Single click
// TODO smth
clickTimeoutId = setTimeout(() => {
clearTimeout(clickTimeoutId);
clickTimeoutId = null;
}, 300);
}
要約すると、同じ要素のsimpleClickイベントとdoubleClickイベントを認識するために、onClickイベントを次のメソッドで処理するだけです。
var EVENT_DOUBLE_CLICK_DELAY = 220; // Adjust max delay btw two clicks (ms)
var eventClickPending = 0;
function onClick(e){
if ((e.detail == 2 ) && (eventClickPending!= 0)) {
// console.log('double click action here ' + e.detail);
clearTimeout(eventClickPending);
eventClickPending = 0;
// call your double click method
fncEventDblclick(e);
} else if ((e.detail === 1 ) && (eventClickPending== 0)){
// console.log('sigle click action here 1');
eventClickPending= setTimeout(function() {
// console.log('Executing sigle click');
eventClickPending = 0
// call your single click method
fncEventClick(e);
}, EVENT_DOUBLE_CLICK_DELAY);
// } else { // do nothing
// console.log('more than two clicks action here ' + e.detail);
}
}
デバウンスを使用して、シングルクリックハンドラーがダブル/マルチクリックを検出しないようにすることができます
テスト:https ://jsfiddle.net/L3sajybp/
HTML
<div id='toDetect'>
Click or double-click me
</div>
<hr/>
<ol id='info'>
</ol>
JS
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this,
args = arguments;
const later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
function debounceSingleClickOnly(func, timeout = 500) {
function eventHandler (event) {
const { detail } = event;
if (detail > 1) {
console.log('no double click for you '+ func.name);
console.log('');
return;
}
func.apply(this, arguments);
}
return debounce(eventHandler, timeout);
}
window.toDetect.addEventListener('click', debounceSingleClickOnly(handleSingleClick));
window.toDetect.addEventListener('dblclick', handleDoubleClick);
function handleS() {
console.log('S func');
console.log(this.id);
}
function handleSingleClick(event) {
console.log('single click');
const divText = document.createElement('li');
divText.appendChild(document.createTextNode('single click'));
window.info.appendChild(divText)
console.group();
console.log('this element was single-clicked: ' + event.target.id);
console.log(this.id);
console.log('');
console.groupEnd();
}
function handleDoubleClick(event) {
console.log('double click');
const divText = document.createElement('li');
divText.appendChild(document.createTextNode('double click'));
window.info.appendChild(divText);
console.group();
console.log('this element was double-clicked: ' + event.target.id);
console.log(this.id);
console.log('');
console.groupEnd();
}
const toggle = () => {
watchDouble += 1;
setTimeout(()=>{
if (watchDouble === 2) {
console.log('double' + watchDouble)
} else if (watchDouble === 1) {
console.log("signle" + watchDouble)
}
watchDouble = 0
},200);
}