'Angular 10 - Not to trigger (click) function on dragging

I have a div with an openlayers map like this:

<div id="map" (click)="getInfo($event)" class="map"></div>

I use dragging with my mouse to navigate around the map, and every time I do that the getInfo function triggers. I just want it to trigger when I single click the map, not when I navigate.

Is it possible? Thanks!



Solution 1:[1]

I had the same problem detecting dragging vs clicking but didn't want to use timed events. If clicked too slow it considers dragging if dragged too quick it considers clicking.

Instead, I used mouse positioning. If x,y position changes between mouse down and click (which is fired on mouse up) it's dragging, if not it's clicking.

Pseudo code:

<div (mousedown)="onMouseDown($event)" (click)="onClick($event)"></div>

.ts file:

mousePosition = {
  x: 0,
  y: 0
};

mouseDown($event: MouseEvent) {
  this.mousePosition.x = $event.screenX;
  this.mousePosition.y = $event.screenY;
}

onClick($event: PointerEvent) {
  if (
    this.mousePosition.x === $event.screenX &&
    this.mousePosition.y === $event.screenY
  ) {
    // Execute Click
  }
}

Solution 2:[2]

https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event

click fires after the mousedown and mouseup events, in that order.

You will probably have to create mousedown and mouseup handlers that set some sort of time interval.

mousedown will start some timer... ie 200ms, and if a mouseup event gets fired within that interval, you can treat it as a click and call getInfo with the event passed to mouseup

if mouseup event gets fired outside of that interval, assume it's a drag'n'drop mouse action and ignore it.

@ViewChild('map') mapElem: ElementRef;

ngAfterViewInit() {
  const mousedown$ = fromEvent(this.mapElem.nativeElement, 'mousedown');

  const mouseup$ = fromEvent(this.mapElem.nativeElement, 'mouseup');

  mousedown$.subscribe(e => {
    const clickTimer$ = timer(200);
    mouseup$.pipe(takeUntil(clickTimer$)).subscribe(e => this.getInfo(e));
  });
}

getInfo(e) {
  console.log('click')
}

https://stackblitz.com/edit/angular-sobuqt

check the console output on this demo I made, you'll see the behavior you want.

Solution 3:[3]

Don't do it!

Other answers give a straight forward answer on how to accomplish handling clicking without handling drag and drop. They are good. But what needs to be taken into an account is the user experience. When you differentiate between click and drag and drop, you become dependant on how smoothly a user device works, and how the user operates the mouse.

For example: if there will be a lag on a computer, laptop or smartphone, delay solution will cause click to be triggered instead of drag. In other situations click will not be triggered, but a drag with a nasty conflict of states that user won't be able to escape.

Moreover, people tend to make small movements with mouse when clicking. The interface will trigger drag on these situations. Or, sometimes people want to drag but will click and navigate to another page. They will need to go back. It is very annoying.

To sum it up - it is a very poor user experience to handle click and drag and drop on the same element at the same time. Sometimes it is not possible to avoid it.

But every time, you can, you should avoid click and drag on the same part of the UI using various workarounds.

There are three true solutions to this problem:

1) Use a separate view to reorder items (a little bit annoying since it duplicates list):

enter image description here

2) Use a drag handler (drag handlers can be small, so still user can click outside it and navigate elsewhere even if he does not want to):

enter image description here

3) Use a switch to enable dragging (best solution). Please notice that on the following mockup drag handlers should be hidden since the checkbox is off. When using this approach whole row can be draggable making it easier to drag and drop. The drag handler can be still shown to indicate what change in the UI (hey user, now you can drag and drop!)

enter image description here

Solution 4:[4]

You can make use of a combination of click and mousedown event bindings to handle this situation. This prevents the click event from being fired if you are dragging your map.

<div id="map" (mousedown)="check($event)" (click)="getInfo($event)" class="map"></div>

On your .ts code,

click: boolean = false;

check() {
  window.setTimeout(this.startCheck(), 1000);
}

startCheck() {
  this.click = true;
}

getInfo(event) {
  if (this.click) {
    // handle the real click event
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Raphaël Balet
Solution 2
Solution 3 Tom Smykowski
Solution 4