call function only once after double click - javascript

I have this div box which calls a function "nextStep" onclick.
function nextStep() {
console.log("NEXT");
}
<div onclick="nextStep()">test</div>
Problem is, if I double click the div box the function will call twice.
That should not be possible.
How can I disallow this?

You can use the time to lock your double click.
Run snippet. If the previous click was less than 0.3 seconds ago, the click will be blocked. You can adjust the amount of time as needed.
function nextStep() {
var $div = $(".test");
if (!$div.data('lockclick') || +new Date() - $div.data('lockclick') > 300) {
console.log("NEXT");
}
$div.data('lockclick', +new Date());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test" onclick="nextStep()">Test</div>

As tagged jquery, you can use jquery's .one method to only allow it to be clicked once.
$("#mydiv").one("click", nextStep);
function nextStep() {
console.log("NEXT")
}
#mydiv { cursor: pointer }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="mydiv">click me</div>
Other alternatives including adding a class with pointer-events:none;

use the event listener 'dblclick' in native javascript
const myDiv= document.querySelector('#my-div');
myDiv.addEventListener('dblclick', function (e) {
console.log('in double click')
});
#my-div{
width:200px;
height:200px;
background: black;
}
<div id='my-div'>test</div>

Here's how to do it in FP style
const once = f => {
let called = false;
return () => {
if (called) return;
called = true
f();
}
}
function nextStep() {
console.log("NEXT");
}
const nextStepOnce = once(nextStep)
<div onclick="nextStepOnce()">test</div>

To avoid the triggering on double click you can save the "clicked" status in a variable and ask for it before executing the function code.
If you want to use the onclick listener again (with single click) you can reset the variable after a short time (300ms should be enough for preventing the double click).
let clicked = false;
function nextStep() {
if (!clicked) {
clicked = true;
console.log("NEXT");
}
setTimeout(function(){
clicked = false;
}, 300);
}
<div onclick="nextStep()">test</div>

Related

How to execute function while handle 2 events at same time?

I have some code that wants to be executed while the user resizing an element and scrolling the window at the same time tow events must occur at the same time to execute the function something like the following but I want to merge them to be like one custom event?
this.on("resize", function() {
$(window).scroll(function() {
figure.setHeight(_this.getHeight());
_this.setHeight(figure.getHeight());
});
});
Explanation : I have used a flag here, initially set to false. As it is given that both events will be happening, we can make a check in one function is the other is happening or not and execute the desired function.
First of all click the button. A console will be generated, that clicked. Now flag is set to true. But myFunction() is not eecuted, coz it will fire only when keydown will take place. In case key is pressed, flag won't be true, and hence myFunction() will not be executed. Now that you know that, button was clicked, so it has the focus. On pressing enter key, it will do perform click and keydown at same time. And myFunction() will get executed. When it gets executed, set flag to false. :)
var flag = false;
$(document).on("keydown", function() {
flag = true;
console.log("key pressed");
});
$('button').click(function() {
console.log("clicked");
if (flag) {
myFunction();
}
});
function myFunction() {
alert("Both at same time");
flag = false;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Click me</button>
From my understanding the resize event is triggered first, and then if the user also scrolls your custom function should be called
let resizeTriggered = false
const myResizeFunc = (event) => {
resizeTriggered = true
}
const myScrollFunc = (event) => {
if (resizeTriggered) {
myCustomFunction()
}
}
const myCustomFunction = () => {
console.log('both are happening')
}
$('#textArea').mousedown(myResizeFunc)
$(window).scroll(myScrollFunc)
div.large {
height:1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea id="textArea"></textarea>
<div class="large">
</div>

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"));

Single click and Double click on the same element, not working; Javascript

I am trying to add a click function that triggers when a button is clicked. I am also trying to figure out how to add a double click function onto the same element, that triggers a different event.
var click = false;
onEvent("image2", "click", function(event) {
click = true;
});
if (click === true) {
setTimeout(function() {
onEvent("image2", "click", function(event) {
setScreen("safeScreen");
console.log("double click");
});
}, 200);
} else {
onEvent("image2", "dblclick", function(event) {
setScreen("safeScreen");
console.log("click");
});
}
This code is completely wrong, but I don't know where to start/correct. What am I doing wrong? I am looking to have the single click not trigger when the user double clicks.
Update:
Try passing a function clicks() to your event listener like so:
onEvent("image2", "click", clicks);
Function clicks() will check if there was a single or double click based on setTimeout function. You can adjust setTimeout via timeout variable and of course you need clickCount variable declared outside clicks() function.
Pure js approach
Try adding two event listeners. Less code, much cleaner. Check this working example.
var selector = document.getElementById('codeorg');
selector.addEventListener('click', clicks);
// Global Scope variable we need this
var clickCount = 0;
// Our Timeout, modify it if you need
var timeout = 500;
// Copy this function and it should work
function clicks() {
// We modify clickCount variable here to check how many clicks there was
clickCount++;
if (clickCount == 1) {
setTimeout(function(){
if(clickCount == 1) {
console.log('singleClick');
// Single click code, or invoke a function
} else {
console.log('double click');
// Double click code, or invoke a function
}
clickCount = 0;
}, timeout || 300);
}
}
// Not important for your needs - pure JS stuff
var button = document.getElementById('button');
button.addEventListener('click', singleClick);
button.addEventListener('dblclick', doubleClick);
function singleClick() {
//console.log('single click');
}
function doubleClick() {
console.log('double click');
}
#codeorg {
margin-bottom: 100px;
}
<h2>Double Click</h2>
<button id="button">Click me</button>
<hr><hr>
<h2>Double click or Single Click</h2>
<button id="codeorg">Click me</button>

Make JQuery event available after a certain amount of time

I'm trying to make one of those annoying popups when leaving your browser. However, I want the event to be available after a certain amount of time. The event should be allowed to trigger after a certain amount of time. I've seen stuff such as delay and setTimeout, but I have no idea how to implement it on my code.
JavaScript:
$(document).on("mouseleave", function (event) {
if (event.pageY < 0) {
$(".leavemodal").fadeIn(600);
}
});
This is not tested but maybe you can try this.
$(document).ready(function() {
canRun = false;
waitPeriod = 1000;// waiting time
setTimeout(function() { canRun = true; }, waitPeriod);
$(document).on("mouseleave", function (event) {
if (!canRun) {
return false;
}
if (event.pageY < 0) {
$(".leavemodal").fadeIn(600);
}
});
});
If you want to use setTimeout() you can do something like this. Click event will be allowed 2 seconds after you mouseleave the element.
var click = false;
$('.el').mouseleave(function() {
if (click == false) {
setTimeout(function() {
console.log('You can click now')
click = true;
$(this).click(function() {
console.log('click')
})
}, 2000)
}
})
.el {
width: 200px;
height: 200px;
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="el"></div>

Prevent click event from firing when dblclick event fires

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);
}

Categories