When a user clicks a certain link I would like to present them with a confirmation dialog. If they click "Yes" I would like to continue the original navigation. One catch: my confirmation dialog is implemented by returning a jQuery.Deferred object which is resolved only when/if the user clicks the Yes button. So basically the confirmation dialog is asynchronous.
So basically I want something like this:
$('a.my-link').click(function(e) {
e.preventDefault(); e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
})
})
Of course I could set a flag and re-trigger click but that is messy as heck. Any natural way of doing this?
Below are the bits from the code that actually worked in Chrome 13, to my surprise.
function handler (evt ) {
var t = evt.target;
...
setTimeout( function() {
t.dispatchEvent( evt )
}, 1000);
return false;
}
This is not very cross-browser, and maybe will be fixed in future, because it feels like security risk, imho.
And i don't know what happens, if you cancel event propagation.
It could be risky but seems to work at the time of writing at least, we're using it in production.
This is ES6 and React, I have tested and found it working for the below browsers. One bonus is if there is an exception (had a couple during the way making this), it goes to the link like a normal <a> link, but it won't be SPA then ofc.
Desktop:
Chrome v.76.0.3809.132
Safari v.12.1.2
Firefox Quantum v.69.0.1
Edge 18
Edge 17
IE11
Mobile/Tablet:
Android v.8 Samsung Internet
Android v.8 Chrome
Android v.9 Chrome
iOs11.4 Safari
iOs12.1 Safari
.
import 'mdn-polyfills/MouseEvent'; // for IE11
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class ProductListLink extends Component {
constructor(props) {
super(props);
this.realClick = true;
this.onProductClick = this.onProductClick.bind(this);
}
onProductClick = (e) => {
const { target, nativeEvent } = e;
const clonedNativeEvent = new MouseEvent('click', nativeEvent);
if (!this.realClick) {
this.realClick = true;
return;
}
e.preventDefault();
e.stopPropagation();
// #todo what you want before the link is acted on here
this.realClick = false;
target.dispatchEvent(clonedNativeEvent);
};
render() {
<Link
onClick={(e => this.onProductClick(e))}
>
Lorem
</Link>
}
}
I solved problem by this way on one of my projects. This example works with some basic event handling like clicks etc. Handler for confirmation must be first handler bound.
// This example assumes clickFunction is first event handled.
//
// you have to preserve called function handler to ignore it
// when you continue calling.
//
// store it in object to preserve function reference
var ignoredHandler = {
fn: false
};
// function which will continues processing
var go = function(e, el){
// process href
var href = $(el).attr('href');
if (href) {
window.location = href;
}
// process events
var events = $(el).data('events');
for (prop in events) {
if (events.hasOwnProperty(prop)) {
var event = events[prop];
$.each(event, function(idx, handler){
// do not run for clickFunction
if (ignoredHandler.fn != handler.handler) {
handler.handler.call(el, e);
}
});
}
}
}
// click handler
var clickFunction = function(e){
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(go.apply(this, e));
};
// preserve ignored handler
ignoredHandler.fn = clickFunction;
$('.confirmable').click(clickFunction);
// a little bit longer but it works :)
If I am understanding the problem correctly, I think you can just update the event to be the original event in that closure you have there. So just set e = e.originalEvent in the .done function.
https://jsfiddle.net/oyetxu54/
MyApp.confirm("confirmation?")
.done(function(){ e = e.originalEvent;})
here is a fiddle with a different example (keep the console open so you can see the messages):
this worked for me in chrome and firefox
I solved this by:
placing a event listener on a parent element
removing the class from the link ONLY when the user confirms
reclicking the link after I have removed the class.
function async() {
var dfd = $.Deferred();
// simulate async
setTimeout(function () {
if (confirm('Stackoverflow FTW')) {
dfd.resolve();
} else {
dfd.reject();
}
}, 0);
return dfd.promise();
};
$('.container').on('click', '.another-page', function (e) {
e.stopPropagation();
e.preventDefault();
async().done(function () {
$(e.currentTarget).removeClass('another-page').click();
});
});
$('body').on('click', function (e) {
alert('navigating somewhere else woot!')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
Somewhere else
</div>
The reason I added the event listener to the parent and not the link itself is because the jQuery's on event will bind to the element until told otherwise. So even though the element does not have the class another-page it still has the event listener attached thus you have to take advantage of event delegation to solve this problem.
GOTCHAS this is very state based. i.e. if you need to ask the user EVERYTIME they click on a link you'll have to add a 2nd listener to readd the another-page class back on to the link. i.e.:
$('body').on('click', function (e) {
$(e.currentTarget).addClass('another-page');
});
side note you could also remove the event listener on container if the user accepts, if you do this make sure you use namespace events because there might be other listeners on container you might inadvertently remove. see https://api.jquery.com/event.namespace/ for more details.
We have a similar requirement in our project and this works for me. Tested in chrome and IE11.
$('a.my-link').click(function(e) {
e.preventDefault();
if (do_something === true) {
e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
do_something = false;
// this allows user to navigate
$(e.target).click();
})
}
})
I edited your code. New features that I added:
Added namespace to event;
After click on element event will be removed by namespace;
Finally, after finish needed actions in "MyApp" section continue propagation by triggering others element "click" events.
Code:
$('a.my-link').on("click.myEvent", function(e) {
var $that = $(this);
$that.off("click.myEvent");
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
$that.trigger("click");
});
});
This is untested but might serve as a workaround for you
$('a.my-link').click(function(e) {
e.preventDefault(); e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
$(this).unbind('click').click()
})
})
Related
In JavaScript, is it possible to distinguish between beforeunload events that were triggered by the user closing a browser tab vs clicking a mailto link?
Basically, I would like to do this:
window.addEventListener("beforeunload", function (e) {
if(browserTabClosed) {
// Do one thing
}
else if (mailtoLinkClicked) {
// Do a different thing
}
}
Found a solution by looking at the event (e below) that gets passed in:
window.addEventListener("beforeunload", function (e) {
// We can use `e.target.activeElement.nodeName`
// to check what triggered the passed-in event.
// - If triggered by closing a browser tab: The value is "BODY"
// - If triggered by clicking a link: The value is "A"
const isLinkClicked = (e.target.activeElement.nodeName === "A");
// If triggered by clicking a link
if (isLinkClicked) {
// Do one thing
}
// If triggered by closing the browser tab
else {
// Do a different thing
}
}
The beforeunload method has an unstable behaviour between browsers, the reason is that browser implementations try to avoid popups and other malicious code runned inside this handler.
There is actually no general (cross-browser) way to detect what triggered the beforeunload event.
Said that, in your case you could just detect a click on the window to discriminate between the two required behaviours:
window.__exit_with_link = false;
window.addEventListener('click', function (e) {
// user clicked a link
var isLink = e.target.tagName.toLowerCase() === 'a';
// check if the link has this page as target:
// if is targeting a popup/iframe/blank page
// the beforeunload on this page
// would not be triggered anyway
var isSelf = !a.target.target || a.target.target.toLowerCase() === '_self';
if (isLink && isSelf) {
window.__exit_with_link = true;
// ensure reset after a little time
setTimeout(function(){ window.__exit_with_link = false; }, 50);
}
else { window.__exit_with_link = false; }
});
window.addEventListener('beforeunload', function (e) {
if (window.__exit_with_link) {
// the user exited the page by clicking a link
}
else {
// the user exited the page for any other reason
}
}
Obviously it is not the proper way, but still working.
At the same way, you could add other handlers to check other reasons the user left the page (eg. keyboard CTRL-R for refresh, etc.)
How to disable the browser back button using javascript in a HTML page. Can we get any callback method trigerred on click of backbutton on browser using Javascript and not using Jquery Mobile library.
Solution would be really appreciated. I tried with few solutions online, but nothing seemed to work.
You should never do that. https://www.irt.org/script/311.htm
By the way, you may just warn the user using window.onbeforeunload.
You can-not actually disable browser back button. And there is no event for capturing the back button click.
If it is really necessary you can do something like that:
(function (global) {
var _extra_hash = "!";
var noBack = function () {
global.location.href += "#";
global.setTimeout(function () {
global.location.href += _extra_hash;
}, 50);
};
global.onhashchange = function () {
if (global.location.hash !== _extra_hash) {
global.location.hash = _extra_hash;
}
};
global.onload = function () {
noBack();
// this is for disabling backspace on page except on input fields and textarea..
/*document.body.onkeydown = function (e) {
var elm = e.target.nodeName.toLowerCase();
if (e.which === 8 && (elm !== 'input' && elm !== 'textarea')) {
e.preventDefault();
}
// stopping event bubbling up the DOM tree..
e.stopPropagation();
};*/
}
})(window);
But the user can still kill the tab. Anyway, It is generally a bad idea overriding the default behavior of web browser.
I have a handler attached to an event and I would like it to execute only if it is triggered by a human, and not by a trigger() method. How do I tell the difference?
For example,
$('.checkbox').change(function(e){
if (e.isHuman())
{
alert ('human');
}
});
$('.checkbox').trigger('change'); //doesn't alert
You can check e.originalEvent: if it's defined the click is human:
Look at the fiddle http://jsfiddle.net/Uf8Wv/
$('.checkbox').change(function(e){
if (e.originalEvent !== undefined)
{
alert ('human');
}
});
my example in the fiddle:
<input type='checkbox' id='try' >try
<button id='click'>Click</button>
$("#try").click(function(event) {
if (event.originalEvent === undefined) {
alert('not human')
} else {
alert(' human');
}
});
$('#click').click(function(event) {
$("#try").click();
});
More straight forward than above would be:
$('.checkbox').change(function(e){
if (e.isTrigger)
{
alert ('not a human');
}
});
$('.checkbox').trigger('change'); //doesn't alert
Currently most of browsers support event.isTrusted:
if (e.isTrusted) {
/* The event is trusted: event was generated by a user action */
} else {
/* The event is not trusted */
}
From docs:
The isTrusted read-only property of the Event interface is a Boolean
that is true when the event was generated by a user action, and false
when the event was created or modified by a script or dispatched via
EventTarget.dispatchEvent().
I think that the only way to do this would be to pass in an additional parameter on the trigger call as per the documentation.
$('.checkbox').change(function(e, isTriggered){
if (!isTriggered)
{
alert ('human');
}
});
$('.checkbox').trigger('change', [true]); //doesn't alert
Example: http://jsfiddle.net/wG2KY/
Accepted answer didn't work for me. It's been 6 years and jQuery has changed a lot since then.
For example event.originalEvent returns always true with jQuery 1.9.x. I mean object always exists but content is different.
Those who use newer versions of jQuery can try this one. Works on Chrome, Edge, IE, Opera, FF
if ((event.originalEvent.isTrusted === true && event.originalEvent.isPrimary === undefined) || event.originalEvent.isPrimary === true) {
//Hey hooman it is you
}
Incase you have control of all your code, no alien calls $(input).focus() than setFocus().
Use a global variable is a correct way for me.
var globalIsHuman = true;
$('input').on('focus', function (){
if(globalIsHuman){
console.log('hello human, come and give me a hug');
}else{
console.log('alien, get away, i hate you..');
}
globalIsHuman = true;
});
// alien set focus
function setFocus(){
globalIsHuman = false;
$('input').focus();
}
// human use mouse, finger, foot... whatever to touch the input
If some alien still want to call $(input).focus() from another planet.
Good luck or check other answers
I needed to know if calls to the oninput handler came from the user or from undo/redo since undo/redo leads to input events when the input's value is restored.
valueInput.oninput = (e) => {
const value = +valueInput.value
update(value)
if (!e.inputType.startsWith("history")) {
console.log('came from human')
save(value)
}
else {
console.log('came from history stacks')
}
}
It turns out that e.inputType is "historyUndo" on undo and "historyRedo" on redo (see list of possible inputTypes).
You can use onmousedown to detect mouse click vs trigger() call.
I would think about a possibility where you check the mouse position, like:
Click
Get mouse position
Overlaps the coords of the button
...
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>
I'm handling both the click and dblclick event on a DOM element. Each one carries out a different command, but I find that when double clicking on the element, in addition to firing the double click event, the click event is also fired twice. What is the best approach for preventing this behavior?
In case anyone else stumbles on this (as I did) looking for an answer, the absolute best solution that I could come up with is the following:
$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 */
}
});
Done. If there is more than one click back to back, the second, third,etc. will not fire. I definitely prefer this to using any sort of timers.
I got myself pointed in this direction by reading this.
Incidentally: I was first researching this problem because I accidentally double clicked a paginated link, and the event fired and finished twice before the callback could happen.
Before coming up with the code above, I had
if e.originalEvent.detail === 2 //return
however, I was able to click on the link 3 times (a triple click), and though the second click didn't fire, the third did
In a comment, you said,
I delay the click handler by 300 ms (a noticeable and annoying delay) and even ...
So it sounds like what you want is that when you click then the DOM should geneate a click event immediately, except not if the click is the first click of a double-click.
To implement this feature, when you click, the DOM would need to be able to predict whether this is the final click or whether it's the first of a double-click (however I don't think is possible in general for the DOM to predict whether the user is about to click again).
What are the two distinct actions which you're trying to take on click and double-click? IMO, in a normal application you might want both events: e.g. single-click to focus on an element and then double-click to activate it.
When you must separate the events, some applications use something other than double-click: for example, they use right-click, or control-click.
You can use UIEvent.detail if you want to detect how many times the element was clicked and fire events based on that.
A simple example:
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
}
});
In this case, it is best to delay the execution of the single click event slightly. Have your double click handler set a variable that the single click event will check. If that variable has a particular value, could be boolDoubleClick == true, then don't fire/handle the single click.
Thanks to all the other answers here as the combination of them seems to provide a reasonable solution for me when the interaction requires both, but mutually exclusive:
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);
Update: I added a generalized version of this approach along with a click polyfill for touch devices to this Github repo with examples:
https://github.com/mckamey/doubleTap.js
AFAIK DOM Level 2 Events makes no specification for double-click.
It doesn't work for me on IE7 (there's a shock), but FF and Opera have no problem managing the spec, where I can attach all actions to the click event, but for double-click just wait till the "detail" attribute of the event object is 2. From the docs: "If multiple clicks occur at the same screen location, the sequence repeats with the detail attribute incrementing with each repetition."
Here is what I did to distinguish within a module
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
I use this solution for my project to prevent click event action, if I had dblclick event that should do different thing.
Note: this solution is just for click and dblclick and not any other thing like tripleclick or etc.
To see proper time between click and double click see this
sorry for my bad English.
I hope it helps :)
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>
It can be achieved via following code
var clickHandler = function(e) { /* put click event handling code here */ };
var doubleclickHandler = function(e) { /* put doubleclick event handling code here */ }
const maxMsBetweenClicks = 300;
var clickTimeoutId = null;
document.addEventListener("dblclick", handleDoubleClick);
document.addEventListener("click", handleSingleClick);
function handleSingleClick(e){
clearTimeout(clickTimeoutId);
clickTimeoutId = setTimeout( function() { clickHandler(e);}, maxMsBetweenClicks);
}
function handleDoubleClick(e){
clearTimeout(clickTimeoutId);
doubleclickHandler(e);
}
I know this is old as heck, but thought I'd post anyhow since I just ran into the same problem. Here's how I resolved it.
$('#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();
});
Here is my simple solution to prevent the second click. Of course, I could restart the timeout when a double click detected, but in reality I never need it.
clickTimeoutId = null;
onClick(e) {
if (clickTimeoutId !== null) {
// Double click, do nothing
return;
}
// Single click
// TODO smth
clickTimeoutId = setTimeout(() => {
clearTimeout(clickTimeoutId);
clickTimeoutId = null;
}, 300);
}
Summarizing, to recognize the simpleClick and doubleClick events on the same element, just treat the onClick event with this method:
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);
}
}
You can use debounce to free the single click handler from detecting the double/multiple clicks
Test at: 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();
}
Output:
const toggle = () => {
watchDouble += 1;
setTimeout(()=>{
if (watchDouble === 2) {
console.log('double' + watchDouble)
} else if (watchDouble === 1) {
console.log("signle" + watchDouble)
}
watchDouble = 0
},200);
}