1

I have two event listeners, one for a Click and a Touch event. They both should run the same function, but only once. If they are both true on some devices it runs the function twice.

I want to be able to click a button and listen for two event listeners but only run the function once if either of them is triggered.

window.addEventListener("click", function(event) {
    myFunction();
});
window.addEventListener("touchstart", function(event) {
    myFunction();
});

function myFunction() {
    console.log("Clicked");
}
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Jake
  • 43
  • 6
  • `touchstart` is not same as `click`. – Rajesh Jul 18 '19 at 08:54
  • @Rajesh yes, I need both of them listening but sometimes they overlap, both being triggered on some devices. I need to join them in case that happened so they only run the function once – Jake Jul 18 '19 at 08:56
  • https://stackoverflow.com/a/7019461/863110 – Mosh Feu Jul 18 '19 at 08:56
  • 1
    You can use a global variable that will keep the state of the function being fired – GalAbra Jul 18 '19 at 08:56
  • What I meant was, `touchstart` is same as `mousedown`. A `click` combines/sums `mousedown`, `mousepress` and `mouseup`. So, you have 2 lifecycle events, so it will be called twice – Rajesh Jul 18 '19 at 08:57
  • Could function body be written in a way that function does not get called if it is already under execution? – techie_28 Jul 18 '19 at 09:02
  • @techie_28 — Since they execute sequentially and not in parallel: No. – Quentin Jul 18 '19 at 10:15

2 Answers2

0

See MDN's article on Supporting both TouchEvent and MouseEvent :

If the browser fires both touch and mouse events because of a single user input, the browser must fire a touchstart before any mouse events. Consequently, if an application does not want mouse events fired on a specific touch target element, the element's touch event handlers should call preventDefault() and no additional mouse events will be dispatched.

Here is a code snippet of the touchmove event handler calling preventDefault().

// touchmove handler
function process_touchmove(ev) {
  // Call preventDefault() to prevent any further handling
  ev.preventDefault();
}
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
0

Assuming the two events are fired almost simultaneously, you can prevent myFunction from executing twice within a pre-defined threshold using the following approach:

// threshold to reset allowing the execution
const _msThreshold = 200;

// date at which the function is last executed
let _myFnExecutedAt = new Date(0);

// your function
function myFunction() {
    console.log("Clicked");
}

// executer that checks if the threshold is exceeded
// to allow your function call and reset the timer
function execute() {
    if (new Date() - _myFnExecutedAt > _msThreshold) {
        myFunction();
        _myFnExecutedAt = new Date();
    }
}

window.addEventListener("click", execute);
window.addEventListener("touchstart", execute);

Bear in mind that you'll have to experiment a bit with the threshold value:

  • If you set it too low, it might be exceeded before the second event registers so both will trigger.
  • If you set it too high, subsequent real clicks/touches might not register.
M0nst3R
  • 5,186
  • 1
  • 23
  • 36