Prevent click event from firing when dblclick event fires

Viewed 51555

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?

14 Answers

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.

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.

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."

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>

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: enter image description here

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

  const toggle = () => {
      watchDouble += 1;
      setTimeout(()=>{
        if (watchDouble === 2) {
          console.log('double' + watchDouble)
        } else if (watchDouble === 1) {
          console.log("signle" + watchDouble)
        }
        watchDouble = 0
      },200);

  }
Related