BugCI/CD

iD Editor Zoom Permanently Stuck After Rapidly Double-Tapping to Close a Touch-Drawn Area

When a user closes a drawn area in the iD OpenStreetMap editor by rapidly double-tapping the final node using touch input on a Windows convertible device, the editor's internal pointer…

Rootlock SRE Engine 5 min read
Diagnostic brief

At a Glance

When a user closes a drawn area in the iD OpenStreetMap editor by rapidly double-tapping the final node using touch input on a Windows convertible device, the editor's internal pointer state becomes desynchronized.

Severity Not rated
Confidence Medium
Frequency Unknown
Impact See analysis

Summary

When a user closes a drawn area in the iD OpenStreetMap editor by rapidly double-tapping the final node using touch input on a Windows convertible device, the editor's internal pointer state becomes desynchronized. The application behaves as though the primary pointer button remains held down, locking zoom into single-finger scroll-to-zoom mode. The broken state persists until the page is refreshed.

Root-Cause Analysis

Confirmed evidence:

  • The failure is reproducible on a touch-enabled Windows convertible (BMAX Y13) running Windows 10 and Microsoft Edge 89.
  • The symptom — zoom behaving as if the left mouse button remains pressed — indicates that iD's internal drag or button-state flag is never cleared after the double-tap sequence completes.
  • Normal zoom and pan functionality returns only on full page reload, confirming the corruption is in ephemeral JavaScript runtime state, not persisted data.

Reasonable inference:

iD uses a combination of pointer events (pointerdown, pointermove, pointerup) and legacy mouse events (mousedown, mouseup) alongside D3's drag behavior to manage map interactions. When a touch-generated double-tap closes an active drawing, the event sequence compresses: a rapid succession of pointerdownpointeruppointerdownpointerup (and the synthesized dblclick) arrives within a very short window. It is likely that:

  1. The double-tap completion triggers the area-close logic while a pointerdown from the second tap is still considered "active" by D3's drag handler or iD's own mouse-state tracker.
  2. The corresponding pointerup for the closing tap is either consumed by the area-close handler before the map's drag/zoom listener can process it, or the event's preventDefault() call suppresses the synthetic mouse event that would normally reset the drag state.
  3. As a result, the zoom/pan layer retains a mousedown-equivalent state indefinitely — no matching mouseup ever resets it.

Alternative causes (cannot confirm without source inspection):

  • A pointerId capture (setPointerCapture) that is never released on the closing element.
  • A race condition in iD's mode state machine where the drawing mode exits before the map behavior's dragend handler fires, orphaning the drag state.
  • Edge-specific touch-action or pointer-coalescing behavior that reorders or drops events compared to other browsers.

What additional evidence would confirm the diagnosis:

  • Browser DevTools event listener breakpoints on pointerup and mouseup during a failing double-tap sequence, to confirm whether those events fire and whether iD's handlers process them.
  • Inspection of iD's internal context._mouse or equivalent state object immediately after the failure.
  • Reproducing in Chrome or Firefox on the same hardware to isolate browser-specific behavior.

Resolution Steps

For end users experiencing the issue:

  1. If zoom breaks during a session, press F5 (or the browser's reload shortcut) to restore normal behavior. Unsaved edits will be lost unless already uploaded.
  2. As a temporary workaround, close the drawn area by single-tapping the endpoint to place the final node, then single-tapping the endpoint a second time with a deliberate pause between taps rather than using a rapid double-tap.
  3. Alternatively, use the keyboard shortcut to finish the area (typically Enter or Escape depending on context) instead of a touch double-tap.

For maintainers investigating the defect:

  1. Instrument iD's pointer/mouse event handlers to log the full event sequence — pointerdown, pointerup, mousedown, mouseup, click, dblclick — captured during a rapid touch double-tap on the closing node.
  2. Audit every code path that terminates drawing mode (iD.modes.DrawArea, iD.behavior.Draw, or equivalents in the current codebase) and verify that each path explicitly resets any D3 drag state and releases pointer capture before exiting.
  3. Add a defensive reset of the map's drag/zoom button state whenever drawing mode exits, regardless of which event triggered the exit. This prevents orphaned state if an event is consumed before the map layer sees it.
  4. Evaluate adding a pointercancel handler to the drawing behavior so that ambiguous pointer sequences always produce a clean state reset.
  5. Test the fix against Edge and Chrome on a Windows touch device to cover both pointer-event stacks.

CLI Commands

Not directly applicable to this browser-based JavaScript issue. Developers cloning and building iD locally can set up the development environment as follows:

git clone https://github.com/openstreetmap/iD.git
cd iD
npm install
npm run all      # build and run tests
npm start        # start local dev server

To enable verbose event logging during debugging in DevTools:

# Open Edge DevTools console and monitor pointer events on the map container
# Paste into the DevTools console after page load:
(function () {
  const target = document.querySelector('.main-map'); // adjust selector as needed
  ['pointerdown','pointerup','pointermove','pointercancel',
   'mousedown','mouseup','click','dblclick'].forEach(type => {
    target.addEventListener(type, e => {
      console.log(`[${type}] pointerId=${e.pointerId} buttons=${e.buttons} target=${e.target.className}`);
    }, { capture: true });
  });
})();

Verification

To confirm the bug is reproducible before a fix:

  1. Open the iD editor in Edge on a touch-capable Windows device.
  2. Begin drawing an area.
  3. Rapidly double-tap the closing node.
  4. Attempt to pinch-to-zoom or use the zoom controls — if zoom is stuck, the bug is present.

To verify a fix resolves the issue:

  1. Apply the patch and rebuild or reload the patched version.
  2. Repeat the rapid double-tap sequence at least five times consecutively.
  3. After each closing double-tap, verify:
  • Pinch-to-zoom functions normally.
  • Single-finger pan does not unexpectedly trigger zoom.
  • The zoom control buttons (+/−) respond correctly.
  1. Confirm that no page reload is required between test iterations.

Rollback indicator:

If zoom remains broken after applying a candidate fix, the drag/button-state reset is either incomplete or occurs on the wrong code path. Re-examine mode-exit sequences and pointer-capture release points.

Prevention

  • State machine invariant enforcement: Add assertions or defensive resets ensuring that the map's drag state and pointer-capture state are always clean when any editing mode exits, regardless of the triggering event type.
  • Regression test: Add an automated integration test (using a framework such as Playwright or Cypress with touch event simulation) that performs a rapid double-tap area-close and asserts that zoom behavior is unaffected afterward.
  • Touch-specific CI coverage: Run iD's test suite with a simulated touch device profile in CI to catch pointer-event-sequence regressions before they reach production.
  • Edge version pinning in test matrix: Include Chromium-based Edge in the browser matrix for touch-input tests, as Edge's pointer event coalescing behavior on Windows can differ from Chrome.
  • pointercancel handling audit: Regularly audit all interactive behaviors that capture pointer events to ensure pointercancel always produces a safe fallback state, preventing future orphaned drag states from any cause.
Developer FirstBuilt for engineers solving real problems
Evidence DrivenTechnical claims tied to available evidence
Automation ReadyStructured for CLI, APIs, and workflows
Privacy FocusedNo unnecessary data collection in this article UI
STAY AHEAD OF ISSUES

Get new root-cause analyses in your inbox

Engineering-focused updates. No fake subscriber counts. Unsubscribe anytime.