58

My issue is, the methods used for both the events is getting triggered when I perform "double click"

For example, I need to perform specific functionality when specific event is triggered.

<a (click)="method1()" (dblclick)="method2()">

Both method1() and method2() are getting triggered when I perform "double click".

Mel
  • 5,837
  • 10
  • 37
  • 42
Durgaachandrakala.E
  • 623
  • 1
  • 5
  • 10
  • 2
    refer to this [**plunker**](https://plnkr.co/edit/NxeLBP55uaibQklOFioQ) – Aravind Jan 17 '18 at 07:04
  • Possible duplicate of [Angular 2 (click) and (dblclick) on the same element not working good?](https://stackoverflow.com/questions/36113861/angular-2-click-and-dblclick-on-the-same-element-not-working-good) – Rahul Singh Jan 17 '18 at 07:05
  • @Aravind: Hello Aravind... Thanks for the plunkr demo of my issue which is the question. I need the solution for that only.. I already got the answer from below by PranavKAndro and it works good. – Durgaachandrakala.E Jan 17 '18 at 16:06
  • @Durgaachandrakala happy to help you – Aravind Jan 17 '18 at 16:07

12 Answers12

44

You can use a timeout and a boolean flag to solve this. Consider the following:

The DOM takes a few milliseconds to recognize the double click.

But it's damn sure that it recognize the double click but the first click is also recognized.

So the logic goes like this.

isSingleClick: Boolean = true;     

method1CallForClick(){
   this.isSingleClick = true;
        setTimeout(()=>{
            if(this.isSingleClick){
                 doTheStuffHere();
            }
         },250)
}
method2CallForDblClick(){
         this.isSingleClick = false;
         doTheStuffDblClickHere();
}

Call the method one in the click event of the element and method 2 in the click event of the element.

yglodt
  • 13,807
  • 14
  • 91
  • 127
PranavKAndro
  • 921
  • 7
  • 9
  • 7
    This doesn't reset `isSingle` back to true. On top of that, *two* single click events are triggered because a double click is also two single clicks. You have to ignore the second one in case you're resetting the flag somewhere. – Benny Bottema Feb 23 '18 at 19:33
32

You can the pure JavaScript call back dblclick, Or as you use in angular 2 : (dblclick)="doubleClickFunction()".

If you have both (click) and (dblclick) on the same element you should add a timeout to your click event callback to avoid it been called when user double click.

Try something like:

.html:

<button (click)="singleClick()" (dblclick)="doubleClick()">click</button>

.js:

singleClick(): void{
    this.timer = 0;
    this.preventSimpleClick = false;
    let delay = 200;

    this.timer = setTimeout(() => {
      if(!this.preventSimpleClick){
        ...
      }
    }, delay);

  }

  doubleClick(): void{
    this.preventSimpleClick = true;
    clearTimeout(this.timer);
    ...
  }
Gili Yaniv
  • 3,073
  • 2
  • 19
  • 34
11
clickCount = 0;
click() {
    this.clickCount++;
    setTimeout(() => {
        if (this.clickCount === 1) {
             // single
        } else if (this.clickCount === 2) {
            // double
        }
        this.clickCount = 0;
    }, 250)
}
ahmad mi
  • 169
  • 1
  • 4
  • 2
    Hi and welcome to stackoverflow, and thank you for answering. While this code might answer the question, can you consider adding some explanation for what the problem was you solved, and how you solved it? This will help future readers to understand your answer better and learn from it. – Plutian Feb 19 '20 at 10:53
9

this is more clean

i found a solution with rxjs, try this

my template have

<div #divElement></div>

And i have in my component.ts

  @ViewChild('divElement') el: ElementRef;

  ngAfterViewInit(): void {
    this.handleClickAndDoubleClick();
  }

  handleClickAndDoubleClick(){
    const el = this.el.nativeElement;
    const clickEvent = fromEvent<MouseEvent>(el, 'click');
    const dblClickEvent = fromEvent<MouseEvent>(el, 'dblclick');
    const eventsMerged = merge(clickEvent, dblClickEvent).pipe(debounceTime(300));
    eventsMerged.subscribe(
      (event) => {
        if(event.type === 'click'){
          //click
        }

        //dblclick
      }
    );
4

I haven't found a way to do it natively in Angular (or Javascript), but here's a generic way to get it working for all your click handlers.

Although conceptually similar to the other answers, the other answers don't handle repeated clicking very well and don't maintain (and reset!) their flags on the right time.

The idea here is to maintain a flag for deciding between the click handler and dblclick handler for a shared context (same element):

  1. The click handler waits for a short time to rule out double clicks
  2. The double click handler blocks single click handlers within that waiting period
  3. Because a double click means two single singles, on double click, again wait for a short period of time before switching the flag back, to make sure the second single click is also blocked

Here's how you use it on your example:

    <a (click)="method1()" (dblclick)="method2()"></a>

Inside your component:

    const clickContext = new ClickContext();

    class YourComponent {
       method1 = clickContext.clickHandler(() => { /* your method1  */ });
       method2 = clickContext.dblClickHandler(() => { /* your method2 */ });
    }

For every context (element) where you need both a click and dblClick handler, you need to create a separate context. Luckily it is a rare requirement.

Now, by using the magic util class I'll go into below, the clicks and doubleclicks are handled properly.

Here's the class that does the flag monitoring and handler execution:

    export class ClickContext {
        private isSingleClick = true;

        clickHandler(handler: ClickHandler): ClickHandler {
            return function() {
                const args = arguments;
                this.isSingleClick = true;
                setTimeout(() => {
                    this.isSingleClick && handler.apply(undefined, args);
                }, 250);
            }
        }

        dblClickHandler(handler: ClickHandler): ClickHandler {
            return function() {
                const args = arguments;
                this.isSingleClick = false;
                setTimeout(() => {
                    this.isSingleClick = true;
                }, 250);
                handler.apply(undefined, args);
            }
        }
    }

    type ClickHandler = (...any) => void;

Notice that care is taken to make sure the original arguments are passed on to the target event handler method1 and method2.

S Panfilov
  • 16,641
  • 17
  • 74
  • 96
Benny Bottema
  • 11,111
  • 10
  • 71
  • 96
1
It needs simple work-round to block single click then do double click.  Check this example 

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>{{title}}</h2>
        <button (click)="singleClick($event)" (dblclick)="doubleClick( $event)"> Click </button>
    </div>
  `,
})
export class App {
  title:string;
  preventSingleClick = false;
  timer: any;
  delay: Number;
  constructor() {
    this.title = `Angular (click) and (dblclick)`
  
  }
  
  singleClick(event) {
    this.preventSingleClick = false;
     const delay = 200;
      this.timer = setTimeout(() => {
        if (!this.preventSingleClick) {
           alert('Single Click Event');
        }
      }, delay);
  }
  
  doubleClick () {
    this.preventSingleClick = true;
    clearTimeout(this.timer);
    alert('Double Click Event')
  }
}

[plunker][1]

  [1]: http://plnkr.co/edit/pbsB0zYQCEY4xrPKngrF?p=preview
Dimuthu
  • 1,611
  • 1
  • 14
  • 16
1

UPDATE (Angular 13): I have created a directive in angular to handle the single and double click event, hope this helps it is just a workaround. How it works is on the code comments. (Open for improvements in the comment section)

@Directive({
  // eslint-disable-next-line @angular-eslint/directive-selector
  selector: '[doubleTap]',
})
export class DBLTapGestureDirective implements OnInit {
  el: HTMLElement;
  private lastOnStart: number = 0;
  //double click threshold fires a double click event if there are 2 click within 230ms
  private DOUBLE_CLICK_THRESHOLD: number = 230;
  //single click threshold fires a single click after 250ms
  // during this time is listening for another click to si if it is a double click
  private SINGLE_CLICK_THRESHOLD: number = 250;
  private isSingleClick: boolean = true;

  //Emit Single and double click event
  @Output() doubleTap = new EventEmitter<void>();
  @Output() singleTap = new EventEmitter<void>();

  constructor(el: ElementRef, private gestureCtrl: GestureController) {
    this.el = el.nativeElement;
  }

  ngOnInit(): void {
    //Create gesture
    const gesture = this.gestureCtrl.create({
      el: this.el,
      threshold: 0,
      gestureName: 'my-gesture',
      onStart: () => {
        //Start Listening
        this.onStart();
      },
    });
    gesture.enable();
  }

  private onStart(): void {
    //get current time
    let now = Date.now();
    //check if time is within the double click threshold if not go to single click
    if (Math.abs(now - this.lastOnStart) <= this.DOUBLE_CLICK_THRESHOLD) {
      //emits double click event
      this.doubleTap.emit();
      //set single click false in order to not fire the single click event
      this.isSingleClick = false;
      //reset the last click timer
      this.lastOnStart = 0;
      //after the double click finished reset the single click to true to start listening again          for single click
      setTimeout(() => {
        this.isSingleClick = true;
      }, this.DOUBLE_CLICK_THRESHOLD);
    } else {
      //set a timeout before sending single click event to wait if there is a double click
      setTimeout(() => {
        if (this.isSingleClick) {
          //fire single click event
          this.singleTap.emit();
          //set single click back to true
          this.isSingleClick = true;
        }
      }, this.SINGLE_CLICK_THRESHOLD);
      //set last click time to now to make the compare in the next click
      this.lastOnStart = now;
    }
  }
}
 <div
      (doubleTap)="like()"
      (singleTap)="openImage()"
      class="message-container">Image</div>
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 25 '21 at 11:47
  • @dardan forget what the bot said, your answer rocks :) thanks! – user998548 Sep 07 '22 at 09:50
0

This is my way to do double click in angular

clickme()
{
  this.count++;
  if(count%2==0)
  { this.doubleclickme(); }
}
this.count is a global variable .
this.doubleclickme() is a an other function in the component .
0

Similar approach as Armando in https://stackoverflow.com/a/60877770/9970629 - but in my case I didn't quite want to use ViewChild.

This approach uses ReplaySubject. Essentially I defined an interface for the click events, and replay subjects for all received single click events, and double click events. Then I define the true single click (single click without double click) to be a merge of the two with the debounce, and a filter to only emit single clicks:

interface ClickRowEvent<T extends any = any> {
  clickType: "single" | "double";
  data: T;
}

@Component({
  selector: "foo",
  template: `
    <div *ngFor let thing of things>
        <div 
           (click)="handleSingleClick(thing) 
           (dblclick)="handleDoubleClick(thing)"
        >
          {{thing}}
        </div>
    </div>
  `
})
export class MyComponent {

  things = ["foo", "bar", "baz"];

  private _singleClickEvents: ReplaySubject<ClickRowEvent>;
  private singleClickEvents$: Observable<ClickRowEvent>;
  private _doubleClickEvents: ReplaySubject<ClickRowEvent>;
  private doubleClickEvents$: ReplaySubject<ClickRowEvent>;

  constructor() {
    this._singleClickEvents = new ReplaySubject<ClickRowEvent>();
    this._doubleClickEvents = new ReplaySubject<ClickRowEvent>();
    
    this.doubleClickEvents$ = this._doubleClickEvents.asObservable();

    const allClicks$ = this._singleClickEvents.asObservable();

    this.singleClickEvents$ = merge(allClicks$, this.doubleClickEvents$).pipe(
      debounceTime(250),
      filter((e) => e.clickType === "single")
    );
  }

  handleSingleClick(data: T): void {
    this._singleClickEvents.next({ clickType: "single", data });
  }

  handleDoubleClick(data: T): void {
    this._doubleClickEvents.next({ clickType: "double", data });
  }
}

And now you can subscribe to all singleClickEvents$ and doubleClickEvents$ without needing additional state management. Of course you could also map back to the data/event if you don't want the extra clickType property.

tplusk
  • 899
  • 7
  • 15
0

you can use event param

html template:

<div (click)="showModal($event)"></div>

Component

showModal(e) {
  if(e.detail == 2) {
    //blabla...
  }
}
0

I just stumbled across this, but i did not like the answers with setTimeout because the reaction time of the single click is then delayed as well. I want an instant reaction to single click, but prevent two single click events on doubleclick.

My case is special though, i WANT to process the single click as well. So this is maybe not really answering the question because i do both - single and double click - the only thing for me is that i do not do the second single click event when the doubleclick fires.

My solution is simple, i simply measure the time it took between two clicks, and if it is above 500ms (the default delay for dblclick), then i will execute the single click code. if not, then the dblclick will trigger and the second single click will not.

clicked = 0;

clickHandler() {
  if (Date.now() - this.clicked > 500) { // change threshold as you need
    this.clicked = Date.now();
    // click logic here
  }
}

dblClickHandler() {
  // dblclick logic here, no extra code needed
}
Peter Kunszt
  • 101
  • 2
  • 5
-3
itemClicked(event: MouseEvent) {
    if (event.type == 'click') {
       
    }
  }

  itemdblClicked(event: MouseEvent) { 
    if (event.type.toLowerCase() == 'dblclick') {
      
    }
  }
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
  • 1
    Welcome to Stack Overflow! While your answer may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. You can edit your answer to add explanations and give an indication of what limitations and assumptions apply. - [From Review](https://stackoverflow.com/review/late-answers/28408862) – Adam Marshall Feb 25 '21 at 15:40