Open Tofandel opened 4 years ago
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:
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()"
Please let me know the version you are using
On Mon, Feb 10, 2020, 5:35 AM OzoneGrif notifications@github.com 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 .
v1.10.2 here. Didn't test it on 2.0.0.
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 notifications@github.com 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 .
Just tested, issue is still present in v2.0.0 Edit: and my fix still works.
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.
thank you! let me try your solution
I'm not able to get the same issue as you guys, someone can make a codepen that has exactly the issue ?
@Tofandel and @OzoneGrif are you listening of the click event directly from the dragscroll element or on a child ?
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.
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.
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>
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>
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.
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