vue-dragscroll icon indicating copy to clipboard operation
vue-dragscroll copied to clipboard

Prevent click event after dragging

Open Tofandel opened this issue 5 years ago • 15 comments
trafficstars

After a drag occurs, the click event should be stopped from firing with stopImmediatePropagation

It otherwise leads to unexpected clicked elements within the scroll area

Tofandel avatar Jan 24 '20 16:01 Tofandel

Same issue here, with an additional note:

On Android/Chrome, the dragscrollstart event is triggered almost instantly, making it difficult to guess if we're on a click (touch) or a real drag.

I had to use a trick to bypass this issue:

  • dragscrollstart: Starts a 100ms timer, this timer sets a variable: dragging = true
  • dragscrollend: Cleans the timer (in case it was still running, which means we're on a click)
  • click.capture: if dragging is true, preventDefault (blocks the click after a drag)

zeroinformatique avatar Feb 10 '20 03:02 zeroinformatique

export default class DragScrollClickFix {

  readonly DRAG_DELAY = 100; // This is the minimal delay to consider a click to be a drag, mostly usefull for touch devices

  timer: NodeJS.Timeout | null = null;
  dragging: boolean = false;

  onDragScrollStart() {
    this.timer = setTimeout(() => this.onTimer(), this.DRAG_DELAY);
  }

  onTimer() {
    this.timer = null;
    this.dragging = true;
  }

  onDragScrollEnd() {
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
    setTimeout(() => this.dragging = false);
  }

  onClickCapture(e: MouseEvent) {
    if (this.dragging) {
      this.dragging = false;
      e.preventDefault();
      e.stopPropagation();
    }
  }
}

In your JS:

dragScrollClickFix = new DragScrollClickFix();

Then in your Vue template:

@click.capture="e => dragScrollClickFix.onClickCapture(e)"
@dragscrollstart="dragScrollClickFix.onDragScrollStart()"
@dragscrollend="dragScrollClickFix.onDragScrollEnd()"

zeroinformatique avatar Feb 10 '20 04:02 zeroinformatique

Please let me know the version you are using

On Mon, Feb 10, 2020, 5:35 AM OzoneGrif [email protected] wrote:

export default class DragScrollClickFix {

readonly DRAG_DELAY = 100; // This is the minimal delay to consider a click to be a drag, mostly usefull for touch devices

timer: NodeJS.Timeout | null = null; dragging: boolean = false;

onDragScrollStart() { this.timer = setTimeout(() => this.onTimer(), this.DRAG_DELAY); }

onTimer() { this.timer = null; this.dragging = true; }

onDragScrollEnd() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } }

onClickCapture(e: MouseEvent) { if (this.dragging) { this.dragging = false; e.preventDefault(); e.stopPropagation(); } } }

In your JS:

dragScrollClickFix = new DragScrollClickFix();

Then in your Vue template:

@click.capture="e => dragScrollClickFix.onClickCapture(e)"
@dragscrollstart="dragScrollClickFix.onDragScrollStart()"
@dragscrollend="dragScrollClickFix.onDragScrollEnd()"

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/donmbelembe/vue-dragscroll/issues/61?email_source=notifications&email_token=ACP46PLKI4PZES5MQHUWUV3RCDKP5A5CNFSM4KLIY64KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOELHGIKI#issuecomment-583951401, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACP46PLUQN7FPG42MQC2M3LRCDKP5ANCNFSM4KLIY64A .

donmbelembe avatar Feb 10 '20 04:02 donmbelembe

v1.10.2 here. Didn't test it on 2.0.0.

zeroinformatique avatar Feb 10 '20 04:02 zeroinformatique

Please try 2.0 and let me know because your issue will be fixed on version 2

On Mon, Feb 10, 2020, 5:45 AM OzoneGrif [email protected] wrote:

v1.10.2 here. Didn't test it on 2.0.0.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/donmbelembe/vue-dragscroll/issues/61?email_source=notifications&email_token=ACP46PO5AZUGL2EYLXK4GODRCDLVDA5CNFSM4KLIY64KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOELHGVYQ#issuecomment-583953122, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACP46PKR2UK6E726H73XDZ3RCDLVDANCNFSM4KLIY64A .

donmbelembe avatar Feb 10 '20 04:02 donmbelembe

Just tested, issue is still present in v2.0.0 Edit: and my fix still works.

zeroinformatique avatar Feb 10 '20 04:02 zeroinformatique

Another possible fix is to do preventDefault() if the position of the mouse release is significantly far away (e.g. at least 5 pixels) from the start position. This is the solution https://github.com/ilyashubin/scrollbooster uses.

Somewhat shorter version of @OzoneGrif's fix: <div v-dragscroll @dragscrollstart="onDragStart" @click.capture="onDragClick"></div>

import { dragscroll } from 'vue-dragscroll';

export default {
  directives: {
    dragscroll,
  },
  data: () => ({
    dragged: false,
    dragTimeout: null,
  }),
  methods: {
    onDragStart() {
      clearTimeout(this.dragTimeout);

      this.dragged = false;
      this.dragTimeout = setTimeout(() => { this.dragged = true; }, 100); // Minimal delay to be regarded as drag instead of click
    },
    onDragClick(e) {
      if (this.dragged) {
        e.preventDefault();
      }

      this.dragged = false;
    },
  },
};

It would be nice if one of these solutions (time comparison or distance comparison) would be implemented by default for links.

jos- avatar Feb 17 '20 20:02 jos-

thank you! let me try your solution

donmbelembe avatar Feb 18 '20 06:02 donmbelembe

I'm not able to get the same issue as you guys, someone can make a codepen that has exactly the issue ?

donmbelembe avatar Feb 19 '20 11:02 donmbelembe

@Tofandel and @OzoneGrif are you listening of the click event directly from the dragscroll element or on a child ?

donmbelembe avatar Feb 19 '20 12:02 donmbelembe

Child. You dragscroll the parent, using your mouse or touchscreen. When you release the drag, it triggers the click on the child component which is beneath the cursor or finger.

zeroinformatique avatar Feb 19 '20 17:02 zeroinformatique

We use this for a thumbtray of nuxt-links below a video. When you click to drag the tray, on mouseup it will follow the link that you cursor is on.

drewbaker avatar May 11 '20 19:05 drewbaker

This worked for me:

On the anchor:

:class="{'no-pointer-event': dragged}" State:

data(){ return { dragged: false } }

Methods: `onDragsStart() { clearTimeout(this.dragTimeout);

this.dragged = false; this.dragTimeout = setTimeout(() => { this.dragged = true; }, 100); }, onDragClick() { setTimeout(() => { this.dragged = false; }, 100); },`

Style: <style lang="scss"> .no-pointer-event{ pointer-events: none; } </style>

arjan-vdw avatar Feb 18 '22 10:02 arjan-vdw

I made a thin component Dragscroll.vue out of it (Vue 3):

<script setup>
// Avoid emitting click event when scrolldragging
let dragging = false;
let timer = null;

function start() {
  timer = setTimeout(() => (dragging = true), 100);
}

function end() {
  clearTimeout(timer);
  setTimeout(() => (dragging = false));
}

function click(event) {
  if (dragging) {
    event.stopPropagation();
  }
}
</script>

<template>
  <div
    v-dragscroll
    @dragscrollstart="start"
    @dragscrollend="end"
    @click.capture="click"
  >
    <slot />
  </div>
</template>

arildm avatar Mar 03 '22 17:03 arildm

I made a thin component Dragscroll.vue out of it (Vue 3):

Just adding this for anyone who uses Typescript. Using ReturnType because setTimeout typing depends on the environment (see here)

<script setup lang="ts">
// Avoid emitting click event when scrolldragging
let dragging = false;
let timer: ReturnType<typeof setTimeout> | null = null;

function start() {
  timer = setTimeout(() => (dragging = true), 100);
}

function end() {
  if (timer) {
    clearTimeout(timer);
  }
  setTimeout(() => (dragging = false));
}

function click(event: MouseEvent) {
  if (!dragging) return;
  event.stopPropagation();
}
</script>

<template>
  <div
    v-dragscroll
    @dragscrollstart="start"
    @dragscrollend="end"
    @click.capture="click"
  >
    <slot />
  </div>
</template>

Let me know I the above code can be improved. I'm no TS expert by any means.

tobijafischer avatar Dec 06 '23 10:12 tobijafischer