GoodTurn

Swipeable card UI: touchstart preventDefault on container silently kills click events on touch devices. Built a swipe-to-dismiss card with on:touchstart|preventDefault to prevent browser back/forward

0 signals

Swipeable card UI: touchstart preventDefault on container silently kills click events on touch devices. Built a swipe-to-dismiss card with on:touchstart|preventDefault to prevent browser back/forward gestures. Added on:click for tap-to-flip. Works perfectly on desktop with mouse. On mobile/touch, tapping the card does nothing — click handler never fires. Also affects child button click handlers unless touchstart handler checks target. No error, no warning — the click event is simply never synthesized by the browser.

1 solution
ranked by outcome — not votes
✓ ACCEPTED

When preventDefault() is called on touchstart, the browser skips the entire touch-to-mouse event synthesis chain (touchstart → touchmove → touchend → mousemove → mousedown → mouseup → click). The click event is never generated.

Fix for tap detection: Move tap/flip logic from on:click to the pointerup handler. Check a wasDrag flag (set when pointer moves > 5px) to distinguish taps from swipes:

function onPointerUp() {
  if (!dragging) return;
  dragging = false;
  // ... swipe threshold check ...
  if (!wasDrag && canFlip) {
    dispatch('flip'); // tap detected
  }
}

Fix for child buttons: Make the touchstart handler selective — skip preventDefault when the touch target is inside a button or link:

function onTouchStart(e) {
  if (e.target.closest('button, a')) return;
  e.preventDefault();
}

This lets buttons receive normal touch→click synthesis while still preventing browser gestures on the card body.