Skip to content

Fix carousel touch handling without Pointer Events#42714

Open
Kevinjohn wants to merge 1 commit into
twbs:mainfrom
Kevinjohn:fix-carousel-touch-delta
Open

Fix carousel touch handling without Pointer Events#42714
Kevinjohn wants to merge 1 commit into
twbs:mainfrom
Kevinjohn:fix-carousel-touch-delta

Conversation

@Kevinjohn

@Kevinjohn Kevinjohn commented Jul 20, 2026

Copy link
Copy Markdown

On browsers without Pointer Events, carousel taps and multi-event swipes can move slides in the wrong direction.

Description

  • Keep the gesture's starting X coordinate separate from its current delta.
  • Reset the delta when each gesture begins.
  • Cover taps and repeated touchmove events with regression tests.

Motivation & Context

  • Observable sequence: activate a carousel control on iOS 12 Safari, then activate it again or swipe through multiple touch-move events.
  • Expected: tapping a control does not also count as a swipe, and a left swipe remains left.
  • Actual: a tap can be interpreted as a right swipe, while repeated touch-move calculations can flip direction.
  • Root cause: the touch path reused _deltaX first for the starting coordinate and then for the calculated delta. Pointer Events bypassed that path, which is why enabling Safari 12's experimental Pointer Events setting avoided the bug.

This restores the separate start-coordinate and delta semantics used before the v5.2 swipe-helper extraction.

Type of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Refactoring (non-breaking change)
  • Breaking change (fix or feature that would change existing functionality)

Checklist

  • I have read the contributing guidelines
  • My code follows the code style of the project
  • My change introduces changes to the documentation
  • I have updated the documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed

Validation

  • pnpm exec eslint js/src/util/swipe.js js/tests/unit/util/swipe.spec.js
  • The regression tests dispatch touchstart, repeated touchmove, and touchend events through the registered listeners.
  • Unpatched main with the regression tests: 809 passed, 2 failed—the tap called rightCallback, and a left swipe called rightCallback instead of leftCallback.
  • Patched branch: pnpm run js-test-karma — 811 of 811 tests passed in Chrome Headless.
  • Bootstrap's BrowserStack matrix includes a real iPhone 7 running iOS 12 for upstream browser validation.

Related issues

Addresses #39721.

@Kevinjohn
Kevinjohn force-pushed the fix-carousel-touch-delta branch from ed32255 to 189bd47 Compare July 20, 2026 03:31
@Kevinjohn

Copy link
Copy Markdown
Author

I appreciate that Issue #39721 was closed as iOS12 will not be covered by v6...

  • but iOS12 is a listed browser supported by v5,
  • the same issue is listed as a candidate for 5.4
  • the fix is small, restores the previous coordinate-handling semantics, and includes targeted regression tests.

  • Bootstrap 5.1 deliberately stored touchStartX and touchDeltaX separately.
  • The v5.2 extraction collapsed both into _deltaX.

The failure is mathematically reproducible:

touchstart: 300       _deltaX = 300
touchmove:  250       _deltaX = 250 - 300 = -50
touchmove:  200       _deltaX = 200 - (-50) = +250  ← reverses

A tap is even clearer: without touchmove, _deltaX remains the screen coordinate and is interpreted as a swipe. The patch restores the exact separation used successfully in v5.1.

I've tested on a devices on current and previous iOS with no regression, but a maintainter with BrowserStack access needs to test on iOS12.


I've written tests to disable Pointer Events, to force the same touch-event path used by Safari 12.

They dispatch actual touchstart, touchmove, and touchend events through Bootstrap’s registered event listeners.

  • The first test simulates a tap at the same screen position and verifies that neither swipe callback runs.
  • The second test simulates a left swipe from 300px to 250px to 200px, ensuring multiple touchmove events are processed.

Against unpatched main, both tests fail: a tap is treated as a right swipe, and the left swipe reverses direction.

This is as close as I can verify that the left-swipe callback runs exactly once and the right-swipe callback never runs, without BrowserStack or a real iOS12 device.

@Kevinjohn
Kevinjohn marked this pull request as ready for review July 20, 2026 03:36
@Kevinjohn
Kevinjohn requested a review from a team as a code owner July 20, 2026 03:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants