Conversation
removeAllRanges() introduced in #844 also fires from _onClick via _refocusCurrentActiveElement, wiping user drag-selections on mouseup. Only clear the selection when it is collapsed, so the stray Firefox anchor is removed but genuine ranges are preserved. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The isCollapsed guard was insufficient - _refocusCurrentActiveElement is also invoked from _onClick on every click of non-focusable body text, and the click event fires after mouseup with the user's drag selection already committed. Move removeAllRanges out of the shared helper and call it only on the blur path, where the Firefox stray anchor bug actually originates. The click path now leaves the selection untouched. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Track mousedown/mouseup state on body and skip the JAWS refocus-on-blur logic while the mouse button is held down. The blur fires between mousedown and mouseup on every click of non-focusable text, and the programmatic focus call aborts the in-progress text selection (and in Firefox plants a stray selection anchor, which was the original #843 symptom). Removing the refocus during active mouse interaction addresses both the original #843 Firefox selection bug and the all-browser selection-blocked regression from #844, without needing removeAllRanges(). JAWS interactions are keyboard-driven, so the group-element refocus behaviour is preserved. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
_onClick fires after mouseup. When the click lands on non-focusable body text, _refocusCurrentActiveElement runs and moves focus to the previously active element, which drops the user's just-formed text selection. Skip the refocus when a non-collapsed selection exists so the user can copy text. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move mousedown/mouseup listeners from body to document, and also reset _isMouseDown on document mouseleave and window blur. This prevents the flag from getting stuck true when the user releases the pointer outside the body or switches window/iframe mid drag, which would otherwise suppress the JAWS refocus-on-blur safety net until the next mousedown/mouseup cycle inside body. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ethan-lp
approved these changes
Jun 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #867
Fix
The
_refocusCurrentActiveElement()helper introduced focus stealing during_onBlurand_onClickflows that broke user text selection in all browsers after #844 landed. This PR replaces theremoveAllRanges()approach with mouse-state-aware refocus suppression:_isMouseDownvia document-levelmousedown/mouseuplisteners_onBlur, skip the JAWS refocus-on-blur safety net while the user is mid drag-selection_onClick, skip the refocus when a non-collapsed selection existsdocument.mouseleaveandwindow.blurWhy this approach
The original Firefox bug (#843) and the new all-browser regression both stem from the same cause: programmatic focus during the mousedown → mouseup window. Firefox plants a stray selection anchor, and every browser drops the active selection when focus moves away from the selected text. Tracking the mouse state lets us leave focus exactly where the user put it during a click/drag, then resume the JAWS safety net once the mouse is released.
removeAllRanges()from #844 is no longer required.JAWS regression check
JAWS uses a virtual cursor driven from the keyboard — it does not generate
mousedown/mouseup. The_isMouseDownflag will never be set during JAWS interaction, so therole=\"group\"refocus behaviour from #697 is preserved unchanged.Hardening note (document vs body)
The existing blur/click handlers stay on
$bodybecause they rely on jQuery delegation. The new mousedown/mouseup listeners attach todocumentso a pointer released outside the body (drag off-page, drag to chrome, drag to another iframe) still cycles the flag.window.bluranddocument.mouseleaveprovide additional safety resets if the cleanupmouseupis missed entirely. In an iframe these all resolve to the iframe's own document/window, so the scope is correct.Testing
_accessibility._isEnabled: trueinconfig.jsonPosted via collaboration with Claude Code