jQuery on 'double click' event (dblclick for mobile) - javascript

I have the following jquery event handling function:
$('.target').on('dblclick', function() {
//respond to double click event
});
My issue is that this event handler doesn't work on touch devices (iPhone, iPad...). Can anyone recommend a reliable alternative to dblclick that works on touch devices and still allows comfortable double click use on full size devices?

I ended up building a custom double click function that will work on both mobile and desktop:
var touchtime = 0;
$(".target").on("click", function() {
if (touchtime == 0) {
// set first click
touchtime = new Date().getTime();
} else {
// compare first click to this click and see if they occurred within double click threshold
if (((new Date().getTime()) - touchtime) < 800) {
// double click occurred
alert("double clicked");
touchtime = 0;
} else {
// not a double click so set as a new first click
touchtime = new Date().getTime();
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="target">Double click me</div>
Alternatively, here is the JSfiddle Demo.

Add this to your index.html
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
I found the mobile zoom function would throw off Jquery's dblclick. Basically it says your viewport wont change effectively shutting off the zoom. This works for me on my Nexus 5 running Chrome.

I know the question has been answered but thought it would be worth putting the solution I use all the time, cheers:
var doubleClicked = false;
$('.target').on('click', function() {
if (doubleClicked) {
//do what you want to do on double click here
}
doubleClicked = true;
setTimeout(() => {
doubleClicked = false;
}, 300);
});

You can bind multiple event listeners on the element and use jQuery's tap event for the touch devices.
$( ".target" ).on({
dbclick: function() {
//do stuff
}, touch: function() {
//do the same stuff
}
});

Thanks for the solution - the only thing I did was add a timeout so that they could be treated as separate events
var touchtime = 0;
var delay = 800;
var action = null;
$(".target").on("click", function() {
/*Double Click */
if((new Date().getTime() - touchtime) < delay){
clearTimeout(action)
alert('dbl');
touchtime=0;
}
/* Single Click */
else{
touchtime = new Date().getTime();
action = setTimeout(function(){
alert('single');
},delay);
}
}));
Although I haven't tested it, might also be worth adding the following to a header section of any HTML <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/> as per: To "user-scalable=no" or not to "user-scalable=no"

The marked answer of #JRulle seems to work only for a single object, if u have many instances with the same class they will be considered as a single object
see the exampleFiddle example
My solution seems to work in cases like that
var touchtime = 0;
$('.target').on('click', function() {
if (touchtime == 0) {
touchtime = new Date().getTime();
} else {
if (((new Date().getTime()) - touchtime) < 800) {
alert("double clicked");
touchtime = 0;
} else {
touchtime = 0;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<p class="target">click me!</p>
<p class="target">then click me!</p>
click link

Multiple targets with own doubleclick counter. The accepted solution has 2 bugs, that are fixed here:
If you click on target and click outside and click on target again within 800 ms, then the doubleclick event fires.
If you have multiple targets, click on different targets within 800 ms, and the doubleclick event fires.
$(document).on("click", function(e)
{
var MAX_DELAY_IN_MS = 800;
var current_time = new Date();
var targets = $(".target");
if ((typeof last_target == "undefined") ||
(last_target == 0))
{
last_target = e.target;
last_click = current_time;
}
else
{
if ((last_target == e.target) &&
((targets.is(e.target) == true) ||
(targets.has(e.target).length !== 0)) &&
(current_time - last_click < MAX_DELAY_IN_MS))
{
alert("double clicked");
}
last_target = 0;
last_click = 0;
}
});
div{display:inline-block; width:30px; height:30px; margin:5px;}
.target{background-color:lime;}
.no_target{background-color:orange;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="target"></div>
<div class="target"></div>
<div class="no_target"></div>
<div class="target"></div>

Programmatically all of the answers given above are fine.
When you double click on mouse button it's just the mass off your finger involved,
so it can be fast...
On the other hand when tapping touch screen usually much larger physical mass is involved.
Larger mass means slower times .
So my approach is "click two times" instead of double click.
Means a global variable e.g var ClickCounter=0;
Inside the function scope
ClickCounter++;
Check if ClickCounter ==2.
Execute your Code.
Reset counter ClickCounter=0
else return false or execute another code

I have an improvement to the code above, that didn´t detect a doubleclick after a single click:
var touchtime = 0;
$(".target").on("click", function() {
if (((new Date().getTime()) - touchtime) < 500) {
alert("double clicked");
}
touchtime = new Date().getTime();
});
This code detects all doubleclicks. I also reduced the touchtime to 500ms (standard doubleclick-time).

The only way is to detect double touch yourselves. You can do it by persisting last touch event timestamp like below:
if (e.touches.length === 1) {
if (this.lastTouchEventTimeStamp) {
const timeInMillisecondsSinceLastTouch = e.timeStamp - this.lastTouchEventTimeStamp;
if (timeInMillisecondsSinceLastTouch > 80 && timeInMillisecondsSinceLastTouch < 400) {
// double tap will be detected here
this.lastTouchEventTimeStamp = undefined;
const dblClickEvent = new DragEvent('dblclick', {
view: window,
bubbles: true,
cancelable: true
});
e.target.dispatchEvent(dblClickEvent);
}
}
this.lastTouchEventTimeStamp = e.timeStamp;
}

Came across this thread and wanted to supply an updated answer.
function doubleClick(event, callback) {
var touchtime = $(event.target).data("touch-time");
if (touchtime == undefined || touchtime == 0) {
// set first click
$(event.target).data("touch-time", new Date().getTime());
} else {
// compare first click to this click and see if they occurred within double click threshold
if (((new Date().getTime()) - touchtime) < 800) {
// double click occurred
callback();
$(event.target).data("touch-time", 0);
} else {
// not a double click so set as a new first click
$(event.target).data("touch-time", new Date().getTime());
}
}
}
It can then be used as follows:
$(selector).click(function(event){
doubleClick(event, function(){
console.log("Hello World");
});
});
This uses the Data Attribute versus a global variable to get/set the Touch Time.
The standard dblclick should work in modern mobile browsers.

This is it... in CoffeeScript
onDblClick = -> "...your function to be fired..."
dbl_click = null
$(element).on 'mousedown', ->
onDblClick() if dbl_click
dbl_click = true
setTimeout () ->
dbl_click = false
, 250

You need to enter "return false" to the end of the function like below
var touchtime = 0;
$('.dbclickopen').click(function() {
if(touchtime == 0) {
//set first click
touchtime = new Date().getTime();
} else {
//compare first click to this click and see if they occurred within double click threshold
if(((new Date().getTime())-touchtime) < 800) {
//double click occurred
touchtime = 0;
window.location = this.href;
} else {
//not a double click so set as a new first click
touchtime = new Date().getTime();
}
}
return false;
});

Related

JavaScript: How prevent dblclick (Double Click) to also fire a single click event

I want avoid that double click also fire a single click event.
A simple solution i found is to delay the click with a timer and destroy the timer if a double click is fired.
var pendingClick;
function myclick(){
clearTimeout(pendingClick);
pendingClick = setTimeout(function(){
console.log('click');
}, 500);
}
function mydblclick(){
clearTimeout(pendingClick);
console.log('double click');
}
<div onclick="myclick()" ondblclick="mydblclick()">Double Click Me!</div>
But this solution is based on timing, if the double click is too slow (>500ms) it also fire a single click.
There is a stable solution for handle both click and double click?
Double-clicking in itself is "based on timing", even in the standard implementation of dblclick / ondblclick. There will always be the issue of a single-click being fired if the double-click is "too slow". What is "too slow"? 300ms? 500ms? 1000ms? Your double-clicks may be only 50ms apart, while my mom's double-clicks are 1-2 seconds apart...
You can get the event and cancel it with the addEventListener like this:
document.addEventListener('dblclick', (event) => { 
event.preventDefault();  
event.stopPropagation(); 
}, true); // With this true, you are cancelling the dblclick event
let pendingClick;
function myclick(){
clearTimeout(pendingClick);
pendingClick = setTimeout(function (){
console.log('click');
}, 500);
}
function mydblclick(){
clearTimeout(pendingClick);
console.log('double click');
}
<div onclick="myclick()" ondblclick="mydblclick()">Double Click Me!</div>
Only work with the 'onclick' function to check if it was one or two clicks and use a variable to count the number of clicks in a given time interval.
Example:
var pendingClick;
var clicked = 0;
var time_dbclick = 500 // 500ms
function myclick(){
clicked++;
if(clicked >= 2){
mydblclick()
clearTimeout(pendingClick)
clicked = 0;
return;
}
clearTimeout(pendingClick)
pendingClick = setTimeout(() => {
console.log('One click!')
clicked = 0;
}, time_dbclick);
}
function mydblclick(){
console.log('double click');
}
<div onclick="myclick()">Double Click Me!</div>
Custom Events instead of inline event handlers
If one prefers to use .addEventListener and .removeEventListener instead of HTML inline-eventhandlers, I would suggest another approach based on Custom Events. That means one would not make use of the standard implementation of "click" and "dblclick", but create own event handling for both:
let lastLeftClick = document.dispatchEvent(new Event("click"));
let doubleclickLength = 300;
function leftClickHandler (e) {
if (e.button != 0) return; // only left clicks shall be handled;
let delaySinceLastClick = e.timeStamp - lastLeftClick.timeStamp;
let eIsDoubleClick = delaySinceLastClick < doubleclickLength;
if (eIsDoubleClick) {
let doubleclickEvt = new CustomEvent("doubleclick", e);
lastLeftClick = lastLeftClick = doubleclickEvt;
document.dispatchEvent(doubleclickEvt);
} else {
let singleClickEvt = new CustomEvent("singleclick", e);
lastLeftClick = singleClickEvt;
document.dispatchEvent(lastLeftClick);
}
}
// adding above click event implementation:
document.addEventListener("click", leftClickHandler);
using the new custom events:
document.addEventListener("singleclick", e=>console.log("single click"));
document.addEventListener("doubleclick", e=>console.log("double click"));

dblClick and click event in d3.js [duplicate]

I've toggled click event to a node and I want to toggle a dbclick event to it as well. However it only triggers the click event when I dbclick on it.
So How do I set both events at the same time?
You have to do your "own" doubleclick detection
Something like that could work:
var clickedOnce = false;
var timer;
$("#test").bind("click", function(){
if (clickedOnce) {
run_on_double_click();
} else {
timer = setTimeout(function() {
run_on_simple_click(parameter);
}, 150);
clickedOnce = true;
}
});
function run_on_simple_click(parameter) {
alert(parameter);
alert("simpleclick");
clickedOnce = false;
}
function run_on_double_click() {
clickedOnce = false;
clearTimeout(timer);
alert("doubleclick");
}
Here is a working JSFiddle
For more information about what delay you should use for your timer, have a look here : How to use both onclick and ondblclick on an element?
$("#test-id").bind("click dblclick", function(){alert("hello")});
Works for both click and dblclick
EDIT --
I think its not possible. I was trying something like this.
$("#test").bind({
dblclick: function(){alert("Hii")},
mousedown: function(){alert("hello")}
});
But its not possible to reach double click without going through single click. I tried mouse down but it does not give any solution.
I pretty much used the same logic as Jeremy D.
However, in my case, it was more neat to solve this thing with anonymous functions, and a little slower double click timeout:
dblclick_timer = false
.on("click", function(d) {
// if double click timer is active, this click is the double click
if ( dblclick_timer )
{
clearTimeout(dblclick_timer)
dblclick_timer = false
// double click code code comes here
console.log("double click fired")
}
// otherwise, what to do after single click (double click has timed out)
else dblclick_timer = setTimeout( function(){
dblclick_timer = false
// single click code code comes here
console.log("single click fired")
}, 250)
})
you need to track double click and if its not a double click perform click action.
Try this
<p id="demo"></p>
<button id='btn'>Click and DoubleClick</button>
<script>
var doubleclick =false;
var clicktimeoutid = 0;
var dblclicktimeoutid = 0;
var clickcheck = function(e){
if(!clicktimeoutid)
clicktimeoutid = setTimeout(function(){
if(!doubleclick)
performclick(e);
clicktimeoutid =0;
},300);
}
var performclick =function(e){
document.getElementById("demo").innerHTML += 'click';
}
var performdblclick = function(e)
{
doubleclick = true;
document.getElementById("demo").innerHTML += 'dblclick';
dblclicktimeoutid = setTimeout(function(){doubleclick = false},800);
};
document.getElementById("btn").ondblclick = performdblclick;
document.getElementById("btn").onclick=clickcheck;
</script>
a slightly different approach - The actual click comparison happens later in the timeOut function, after a preset interval... till then we simply keep tab on the flags.
& with some simple modifications (click-counter instead of flags) it can also be extended to any number of rapid successive clicks (triple click, et al), limited by practicality.
var clicked = false,
dblClicked = false,
clickTimer;
function onClick(param){
console.log('Node clicked. param - ',param);
};
function onDoubleClick(param){
console.log('Node Double clicked. param - ',param);
};
function clickCheck(param){
if (!clicked){
clicked = true;
clickTimer = setTimeout(function(){
if(dblClicked){
onDoubleClick(param);
}
else if(clicked){
onClick(param);
}
clicked = false;
dblClicked = false;
clearTimeout(clickTimer);
},150);
} else {
dblClicked = true;
}
};

Eliminate Recursion on Events Triggered Inside Mousewheel Event

I'm working on a project that's using a sort of dummy pagination. The body is set to overflow: hidden and currently the only way to navigate the pages is by physically clicking on either links in the nav pane, or on sroll-down/scroll-up buttons. Here's an idea of the events that are triggered when those elements are physically clicked:
var links = $('#topnav, .top-mid a'), l = links.length - 1;
var id = 0;
$('.scrollDown, .scrollUp, .top-mid a, body.home #topnav').click(function (e) {
e.preventDefault();
var $this = $(this);
if ($this.is('.scrollDown') && id < l) id++;
if ($this.is('.scrollUp') && id > 0) id--;
if ($this.is('#topnav, .top-mid a')) id = links.index(this);
// Body is animated down or up and elements are
// shown or hidden depending on what was clicked and
// and what the var id is currently equal to
});
The idea is to trigger exactly ONE click of the scroll button on a mousewheel event. So something close to as simple as this, but that actually works:
$(window).on('mousewheel', function(e){ // I realize this will not work in FF
var evt = e.originalEvent.wheelDelta;
console.log(evt);
// Scrolling Down
if (evt < 0) {
$('.scrollDown').click(); // This fires recursively as long as wheelDelta !== 0
}
});
How can I either force wheelDelta to only increment or decrement by one, or, barring that, how can I eliminate the recursion on the click event?
I've been at this for a while, and read lots of posts and haven't been able to crack it. I've also tried fullPage.js, but it's rather heavy and doesn't really suit my project for other various reasons.
I finally solved this, and of course it turned out to be quite simple. It was a matter of toggling a boolean value inside the click() event, but only after all the animations had taken place. Like this:
var scrolled = false;
$(window).on('mousewheel', function(e){
var evt = e.originalEvent.wheelDelta;
// Scrolling Down - Only fire the click event if it hasn't already fired
if (evt < 0 && !scrolled) {
$('.scrollDown').click();
// Scrolling Up
} else if (evt > 0 && !scrolled) {
$('.scrollUp').click();
}
});
// Toggle the scrolled variable inside the original click event
$('.scrollDown, .scrollUp').click(function (e) {
e.preventDefault();
var $this = $(this);
if ($this.is('.scrollDown') && id < l) {
id++;
scrolled = true;
setTimeout(function(){
scrolled = false;
}, 1500);
}
if ($this.is('.scrollUp') && id > 0) {
id--;
scrolled = true;
setTimeout(function(){
scrolled = false;
}, 1500);
}
// Other events here
// The timeout has to be set high enough to assure
// that the mousewheel event is finished
});

Detect single tap in UIWebView, but still support text selection and links

I'm using JavaScript to detect taps in a page I'm showing in a UIWebView, like so:
<div id="wrapper">
Apple
</div>
<script>
document.getElementById("wrapper").addEventListener('click', function() {
document.location = 'internal://tap';
}, false);
</script>
I'm intercepting links with my web view delegate, and look for "internal://tap". When I get that, I prevent the web view from navigating, and respond to the tap. However doing this I lose the ability to select text. Tapping the link does still work correctly.
In fact, just adding an event listener for 'click' removes the ability to select text, even if the handler doesn't attempt to change the document location.
Any idea what I'm doing wrong?
Apparently if you put a click listener on an element, you can no longer select text within that element on iOS. My solution was to detect taps using a combination of touchstart, touchmove, and touchend events, along with a timer to ignore multi-taps, and checking the current document selection to make sure a selection event is not going on.
Here's the JS code I used:
SingleTapDetector = function(element, handler) {
this.element = element;
this.handler = handler;
element.addEventListener('touchstart', this, false);
};
SingleTapDetector.prototype.handleEvent = function(event) {
switch (event.type) {
case 'touchstart': this.onTouchStart(event); break;
case 'touchmove': this.onTouchMove(event); break;
case 'touchend': this.onTouchEnd(event); break;
}
};
SingleTapDetector.prototype.onTouchStart = function(event) {
this.element.addEventListener('touchend', this, false);
document.body.addEventListener('touchmove', this, false);
this.startX = this.currentX = event.touches[0].clientX;
this.startY = this.currentY = event.touches[0].clientY;
this.startTime = new Date().getTime();
};
SingleTapDetector.prototype.onTouchMove = function(event) {
this.currentX = event.touches[0].clientX;
this.currentY = event.touches[0].clientY;
};
SingleTapDetector.prototype.onTouchEnd = function(event) {
var that = this;
// Has there been one or more taps in this sequence already?
if (this.tapTimer) {
// Reset the timer to catch any additional taps in this sequence
clearTimeout(this.tapTimer);
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
}, 300);
} else {
// Make sure the user didn't move too much
if (Math.abs(this.currentX - this.startX) < 4 &&
Math.abs(this.currentY - this.startY) < 4) {
// Make sure this isn't a long press
if (new Date().getTime() - this.startTime <= 300) {
// Make sure this tap wasn't part of a selection event
if (window.getSelection() + '' == '') {
// Make sure this tap is in fact a single tap
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
// This is a single tap
that.handler(event);
}, 300);
}
}
}
}
};
new SingleTapDetector(document.body, function(event) {
document.location = "internal://tap";
});
There is no need to use Javascript for this, it's overkill when the UIGestureRecognizerDelegate has adequate methods. All you need to do is make sure that when text selection is taking place, the tap recogniser isn't triggered.
- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
BOOL hasTap = ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]);
BOOL hasLongTouch = ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]);
if (hasTap && hasLongTouch) {
// user is selecting text
return NO;
}
return YES;
}
That takes care of text selection, and links should work fine anyway (at least they do for me).

How to differentiate single click event and double click event?

I have a single button in li with id "my_id". I attached two jQuery events with this element
1.
$("#my_id").click(function() {
alert('single click');
});
2.
$("#my_id").dblclick(function() {
alert('double click');
});
But every times it gives me the single click
Instead of utilizing more ad-hoc states and setTimeout, turns out there is a native property called detail that you can access from the event object!
element.onclick = event => {
if (event.detail === 1) {
// it was a single click
} else if (event.detail === 2) {
// it was a double click
}
};
Modern browsers and even IE-9 supports it :)
Source: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
The behavior of the dblclick event is explained at Quirksmode.
The order of events for a dblclick is:
mousedown
mouseup
click
mousedown
mouseup
click
dblclick
The one exception to this rule is (of course) Internet Explorer with their custom order of:
mousedown
mouseup
click
mouseup
dblclick
As you can see, listening to both events together on the same element will result in extra calls to your click handler.
You need to use a timeout to check if there is an another click after the first click.
Here is the trick:
// Author: Jacek Becela
// Source: http://gist.github.com/399624
// License: MIT
jQuery.fn.single_double_click = function(single_click_callback, double_click_callback, timeout) {
return this.each(function(){
var clicks = 0, self = this;
jQuery(this).click(function(event){
clicks++;
if (clicks == 1) {
setTimeout(function(){
if(clicks == 1) {
single_click_callback.call(self, event);
} else {
double_click_callback.call(self, event);
}
clicks = 0;
}, timeout || 300);
}
});
});
}
Usage:
$("button").single_double_click(function () {
alert("Try double-clicking me!")
}, function () {
alert("Double click detected, I'm hiding")
$(this).hide()
})
<button>Click Me!</button>
EDIT:
As stated below, prefer using the native dblclick event: http://www.quirksmode.org/dom/events/click.html
Or the one provided by jQuery: http://api.jquery.com/dblclick/
The modern correct answer is a mix between the accepted answer and #kyw 's solution.
You need a timeout to prevent that first single click and the event.detail check to prevent the second click.
const button = document.getElementById('button')
let timer
button.addEventListener('click', event => {
if (event.detail === 1) {
timer = setTimeout(() => {
console.log('click')
}, 200)
}
})
button.addEventListener('dblclick', event => {
clearTimeout(timer)
console.log('dblclick')
})
<button id="button">Click me</button>
A simple function. No jquery or other framework is required. Pass your functions as parameters
<div onclick="doubleclick(this, function(){alert('single')}, function(){alert('double')})">click me</div>
<script>
function doubleclick(el, onsingle, ondouble) {
if (el.getAttribute("data-dblclick") == null) {
el.setAttribute("data-dblclick", 1);
setTimeout(function () {
if (el.getAttribute("data-dblclick") == 1) {
onsingle();
}
el.removeAttribute("data-dblclick");
}, 300);
} else {
el.removeAttribute("data-dblclick");
ondouble();
}
}
</script>
I'm afraid that the behaviour is browser dependent:
It is inadvisable to bind handlers to
both the click and dblclick events for
the same element. The sequence of
events triggered varies from browser
to browser, with some receiving two
click events before the dblclick and
others only one. Double-click
sensitivity (maximum time between
clicks that is detected as a double
click) can vary by operating system
and browser, and is often
user-configurable.
http://api.jquery.com/dblclick/
Running your code in Firefox, the alert() in the click() handler prevents you from clicking a second time. If you remove such alert, you get both events.
Well in order to double click (click twice) you must first click once. The click() handler fires on your first click, and since the alert pops up, you don't have a chance to make the second click to fire the dblclick() handler.
Change your handlers to do something other than an alert() and you'll see the behaviour. (perhaps change the background color of the element):
$("#my_id").click(function() {
$(this).css('backgroundColor', 'red')
});
$("#my_id").dblclick(function() {
$(this).css('backgroundColor', 'green')
});
This answer is made obsolete through time, check #kyw's solution.
I created a solution inspired by the gist posted by #AdrienSchuler. Use this solution only when you want to bind a single click AND a double click to an element. Otherwise I recommend using the native click and dblclick listeners.
These are the differences:
Vanillajs, No dependencies
Don't wait on the setTimeout to handle the click or doubleclick handler
When double clicking it first fires the click handler, then the doubleclick handler
Javascript:
function makeDoubleClick(doubleClickCallback, singleClickCallback) {
var clicks = 0, timeout;
return function() {
clicks++;
if (clicks == 1) {
singleClickCallback && singleClickCallback.apply(this, arguments);
timeout = setTimeout(function() { clicks = 0; }, 400);
} else {
timeout && clearTimeout(timeout);
doubleClickCallback && doubleClickCallback.apply(this, arguments);
clicks = 0;
}
};
}
Usage:
var singleClick = function(){ console.log('single click') };
var doubleClick = function(){ console.log('double click') };
element.addEventListener('click', makeDoubleClick(doubleClick, singleClick));
Below is the usage in a jsfiddle, the jQuery button is the behavior of the accepted answer.
jsfiddle
Another simple Vanilla solution based on the A1rPun answer (see his fiddle for the jQuery solution, and both are in this one).
It seems that to NOT trigger a single-click handler when the user double-clicks, the single-click handler is necessarily triggered after a delay...
var single = function(e){console.log('single')},
double = function(e){console.log('double')};
var makeDoubleClick = function(e) {
var clicks = 0,
timeout;
return function (e) {
clicks++;
if (clicks == 1) {
timeout = setTimeout(function () {
single(e);
clicks = 0;
}, 250);
} else {
clearTimeout(timeout);
double(e);
clicks = 0;
}
};
}
document.getElementById('btnVanilla').addEventListener('click', makeDoubleClick(), false);
How to differentiate between single clicks and double clicks on one and the same element?
If you don't need to mix them, you can rely on click and dblclick and each will do the job just fine.
A problem arises when trying to mix them: a dblclick event will actually trigger a click event as well, so you need to determine whether a single click is a "stand-alone" single click, or part of a double click.
In addition: you shouldn't use both click and dblclick on one and the same element:
It is inadvisable to bind handlers to both the click and dblclick events for the same element. The sequence of events triggered varies from browser to browser, with some receiving two click events before the dblclick and others only one. Double-click sensitivity (maximum time between clicks that is detected as a double click) can vary by operating system and browser, and is often user-configurable.
Source: https://api.jquery.com/dblclick/
Now on to the good news:
You can use the event's detail property to detect the number of clicks related to the event. This makes double clicks inside of click fairly easy to detect.
The problem remains of detecting single clicks and whether or not they're part of a double click. For that, we're back to using a timer and setTimeout.
Wrapping it all together, with use of a data attribute (to avoid a global variable) and without the need to count clicks ourselves, we get:
HTML:
<div class="clickit" style="font-size: 200%; margin: 2em; padding: 0.25em; background: orange;">Double click me</div>
<div id="log" style="background: #efefef;"></div>
JavaScript:
<script>
var clickTimeoutID;
$( document ).ready(function() {
$( '.clickit' ).click( function( event ) {
if ( event.originalEvent.detail === 1 ) {
$( '#log' ).append( '(Event:) Single click event received.<br>' );
/** Is this a true single click or it it a single click that's part of a double click?
* The only way to find out is to wait it for either a specific amount of time or the `dblclick` event.
**/
clickTimeoutID = window.setTimeout(
function() {
$( '#log' ).append( 'USER BEHAVIOR: Single click detected.<br><br>' );
},
500 // how much time users have to perform the second click in a double click -- see accessibility note below.
);
} else if ( event.originalEvent.detail === 2 ) {
$( '#log' ).append( '(Event:) Double click event received.<br>' );
$( '#log' ).append( 'USER BEHAVIOR: Double click detected.<br>' );
window.clearTimeout( clickTimeoutID ); // it's a dblclick, so cancel the single click behavior.
} // triple, quadruple, etc. clicks are ignored.
});
});
</script>
Demo:
JSfiddle
Notes about accessibility and double click speeds:
As Wikipedia puts it "The maximum delay required for two consecutive clicks to be interpreted as a double-click is not standardized."
No way of detecting the system's double-click speed in the browser.
Seems the default is 500 ms and the range 100-900mms on Windows (source)
Think of people with disabilities who set, in their OS settings, the double click speed to its slowest.
If the system double click speed is slower than our default 500 ms above, both the single- and double-click behaviors will be triggered.
Either don't use rely on combined single and double click on one and the same item.
Or: add a setting in the options to have the ability to increase the value.
It took a while to find a satisfying solution, I hope this helps!
Here's an alternative of jeum's code for an arbitrary number of events:
var multiClickHandler = function (handlers, delay) {
var clicks = 0, timeout, delay = delay || 250;
return function (e) {
clicks++;
clearTimeout(timeout);
timeout = setTimeout(function () {
if(handlers[clicks]) handlers[clicks](e);
clicks = 0;
}, delay);
};
}
cy.on('click', 'node', multiClickHandler({
1: function(e){console.log('single clicked ', e.cyTarget.id())},
2: function(e){console.log('double clicked ', e.cyTarget.id())},
3: function(e){console.log('triple clicked ', e.cyTarget.id())},
4: function(e){console.log('quadro clicked ', e.cyTarget.id())},
// ...
}, 300));
Needed this for a cytoscape.js app.
Use the excellent jQuery Sparkle plugin. The plugin gives you the option to detect first and last click. You can use it to differentiate between click and dblclick by detecting if another click was followed by the first click.
Check it out at http://balupton.com/sandbox/jquery-sparkle/demo/
I wrote a simple jQuery plugin that lets you use a custom 'singleclick' event to differentiate a single-click from a double-click:
https://github.com/omriyariv/jquery-singleclick
$('#someDiv').on('singleclick', function(e) {
// The event will be fired with a small delay.
console.log('This is certainly a single-click');
}
I like to avoid jquery (and other 90-140k libs), and as noted browsers handle onclick first, so here is what I did on a website I created (this example also covers getting a clicked location local x y )
clicksNow-0; //global js, owell
function notify2(e, right) { // called from onclick= and oncontextmenu= (rc)
var x,y,xx,yy;
var ele = document.getElementById('wrap');
// offset fixed parent for local win x y
var xxx= ele.offsetLeft;
var yyy= ele.offsetTop;
//NScape
if (document.layers || document.getElementById&&!document.all) {
xx= e.pageX;
yy= e.pageY;
} else {
xx= e.clientX;
yy= e.clientY;
}
x=xx-xxx;
y=yy-yyy;
clicksNow++;
// 200 (2/10ths a sec) is about a low as i seem to be able to go
setTimeout( "processClick( " + right + " , " + x + " , " + y + ")", 200);
}
function processClick(right, x, y) {
if (clicksNow==0) return; // already processed as dblclick
if (clicksNow==2) alert('dbl');
clicksNow=0;
... handle, etc ...
}
hope that helps
Based on Adrien Schuler (thank you so much!!!) answer, for Datatables.net and for many uses, here is a modification:
Function
/**
* For handle click and single click in child's objects
* #param {any} selector Parents selector, like 'tr'
* #param {any} single_click_callback Callback for single click
* #param {any} double_click_callback Callback for dblclick
* #param {any} timeout Timeout, optional, 300 by default
*/
jQuery.fn.single_double_click = function (selector, single_click_callback, double_click_callback, timeout) {
return this.each(function () {
let clicks = 0;
jQuery(this).on('click', selector, function (event) {
let self = this;
clicks++;
if (clicks == 1) {
setTimeout(function () {
if (clicks == 1) {
single_click_callback.call(self, event);
} else {
double_click_callback.call(self, event);
}
clicks = 0;
}, timeout || 300);
}
});
});
}
Use
$("#MyTableId").single_double_click('tr',
function () { // Click
let row = MyTable.row(this);
let id = row.id();
let data = row.data();
console.log("Click in "+id+" "+data);
},
function () { // DBLClick
let row = MyTable.row(this);
let id = row.id();
let data = row.data();
console.log("DBLClick in "+id+" "+data);
}
);
let clickTimes = 0;
let timer = null;
roundBox.click = function (e) {
clearTimeout(timer);
timer = setTimeout(() => { // 单击事件
console.log("single click");
}, 600);
clickTimes++;
if (clickTimes == 2) { // 双击
clearTimeout(timer);
clickTimes = 0;
console.log("double click");
toggleExpanded(id);
}
}
this worked for me–
var clicked=0;
function chkBtnClcked(evnt) {
clicked++;
// wait to see if dblclick
if (clicked===1) {
setTimeout(function() {
clicked=0;
.
.
}, 300); // test for another click within 300ms
}
if (clicked===2) {
stopTimer=setInterval(function() {
clicked=0;
.
.
}, 30*1000); // refresh every 30 seconds
}
}
usage–
<div id="cloneimages" style="position: fixed;" onclick="chkBtnClcked(evnt)" title="Click for next pic; double-click for slide show"></div>
Just posting the native HTML answer just in case the need is to be easy and HTML.
<p ondblclick="myFunction()" id = 'id'>Double-click me</p>
This of course has native Jquery options. ie... $('#id').attr('ondblclick',function(){...}) or, as stated previously, $('#id').dblclick(function(){...});
I know this is old, but below is a JS only example of a basic loop counter with a single timer to determine a single vs double click. Hopefully this helps someone.
var count = 0;
var ele = document.getElementById("my_id");
ele.addEventListener('click', handleSingleDoubleClick, false);
function handleSingleDoubleClick()
{
if(!count) setTimeout(TimerFcn, 400); // 400 ms click delay
count += 1;
}
function TimerFcn()
{
if(count > 1) console.log('you double clicked!')
else console.log('you single clicked')
count = 0;
}
Try this code
let click = 0;
element.onclick = (event) => {
click++;
console.log(click);
setTimeout(() => {
click = 0;
}, 300);
if (click === 2) {
console.log("double Click");
click = 0;
console.log(click);
}
};
If you want to distinguish between a single and double click, the event handler of the single click has to wait until it is proven, that the single click is not the beginning of a double click. This makes single clicks lagging. The example shows this.
var distinguish = (() => {
var target = null;
var timeout = null;
return (element, action) => {
element.addEventListener ('click', e => {
if (e.target === target) {
clearTimeout (timeout);
target = null;
timeout = null;
action ('double');
} else {
target = e.target;
timeout = setTimeout (() => {
target = null;
timeout = null;
action ('single');
}, 500);
}
});
};
})();
var button = document.getElementById ('button');
distinguish (button, kind => console.log (kind + ' click'));
<input id="button" type="button" value="click">
Pure JS, to truly differentiate single- vs double-click, (e.g. not triggering both at the same time). I'm using this combination of the native event.detail and a custom delay, to prevent the single-click from firing, if it gets cancelled by a double-click.
This approach is also very performance friendly, as it doesn't start a new timer every time we click in quick succession.
The only minor thing (as with some of the other solutions too), is that it may still fire both events, if the user double-clicks very very slowly. This can be prevented by highering the delay, but that would make single-clicking feel even more laggy.
Also there is a lot of differences in the suggested answers as to how they handle quick multi-clicking. So to make things clear, here is what happens in every consecutive click with this approach:
triggers a slightly delayed single-click, if it isn't cancelled by a doubleclick
triggers double-click
nothing
triggers double-click
nothing
...(every 2nd click is a doubleclick, which feels very natural)
I included a snippet so you can test it for yourself.
document.querySelector('button').addEventListener('click', single_or_double);
let isSingleClick; // flag to allow or cancel single clicks
function single_or_double(){
if (isSingleClick = event.detail == 1){ //check for a singleclick and store flag globally at the same time
setTimeout(() => {
if(isSingleClick){ //check if the flag is still set after the delay
console.log("single");
}
}, 200); // singleclick delay in milliseconds
}
else if (event.detail == 2) {
console.log("double");
}
}
<button>Single OR Double-Click</button>

Categories