Symptoms
After v0.31.3-beta, long-press → Navigate works (the geocode-bar Navigate is now tappable, bar hides via #176 fix). But once guidance starts, the Stop button on the guidance pill doesn't respond to taps — "like it's not a button".
Root cause
updateGuidance(e, state, map) is called from location.ts:onLocationFound on every accepted GPS fix (~1 Hz on a moving phone). Its last action is render(), which does:
panelEl.innerHTML = '';
// ...rebuild rows...
appendButton('Stop', ...); // creates a NEW <button> element
So every second:
- The current Stop button is destroyed (innerHTML wipes it).
- A fresh
<button> element is created with a fresh addEventListener('click', onStop).
- The new button is appended to the panel.
iOS Safari's touch-to-click synthesis tracks the DOM element that received touchstart. If the element is destroyed and replaced before touchend, no click is synthesized — the user's tap is silently dropped. Most taps land in the rebuild window because GPS fixes fire faster than typical tap-and-release timing on a moving device.
Fix
Use event delegation: attach a single click listener to the persistent panelEl once at control creation. The listener checks event.target.closest('.guidance-btn') and calls onStop if matched. The listener never re-attaches, so it survives every render. Buttons can come and go without breaking the touch path.
Also add type="button" to the buttons defensively (avoid any implicit form-submit semantics).
Acceptance
Symptoms
After v0.31.3-beta, long-press → Navigate works (the geocode-bar Navigate is now tappable, bar hides via #176 fix). But once guidance starts, the Stop button on the guidance pill doesn't respond to taps — "like it's not a button".
Root cause
updateGuidance(e, state, map)is called fromlocation.ts:onLocationFoundon every accepted GPS fix (~1 Hz on a moving phone). Its last action isrender(), which does:So every second:
<button>element is created with a freshaddEventListener('click', onStop).iOS Safari's touch-to-click synthesis tracks the DOM element that received
touchstart. If the element is destroyed and replaced beforetouchend, noclickis synthesized — the user's tap is silently dropped. Most taps land in the rebuild window because GPS fixes fire faster than typical tap-and-release timing on a moving device.Fix
Use event delegation: attach a single
clicklistener to the persistentpanelElonce at control creation. The listener checksevent.target.closest('.guidance-btn')and callsonStopif matched. The listener never re-attaches, so it survives every render. Buttons can come and go without breaking the touch path.Also add
type="button"to the buttons defensively (avoid any implicit form-submit semantics).Acceptance