A browser selection identifies DOM nodes and offsets. Those nodes can disappear when a renderer replaces a paragraph, a collaborative update inserts text, or a paste is normalized. Saving a Range object alone is therefore not a durable caret strategy. A document editor needs a model bookmark, such as block ID and text offset, and a rule for mapping it through each transaction. Selection direction can matter for shift-selection. A deleted block needs an explicit fallback position rather than a stale node reference.
Selection Bookmarks and Transaction Mapping
Working case
Reviewer 47 selects characters 29 through 47 in a case note and applies emphasis. The document transform splits text runs, so the original text node is replaced. The editor maps the selection to block 62 offsets and restores it after rendering. Another reviewer inserts five characters before the range; the bookmark shifts. If the whole block is deleted, focus returns to the nearest surviving editable block. The browser’s visible caret and model offsets remain consistent after undo and after a remote revision arrives.
Implementation boundary
function mapOffsetAfterInsert(offset, insertAt, length) {
return offset >= insertAt ? offset + length : offset;
}
console.log(mapOffsetAfterInsert(47, 29, 5));
// Output: 52Store selection as anchor and focus positions in the document model, not just start and end DOM nodes. For each insert, delete, split, and merge transaction, define a mapping from old positions to new positions. Use stable block IDs and clamp offsets to valid text length. Convert DOM selection to model positions at the input boundary, then restore DOM selection after the committed render. Avoid collapsing a backward selection accidentally. During composition, minimize selection restoration unless the browser requires it. Test nested formatting, empty blocks, bidirectional text, and a node removed by remote change.
Cost and boundaries
Mapping a position through one local operation can be O(1) or proportional to affected blocks; scanning the entire DOM after every key can become O(n). Long histories of transactions may require periodic checkpoints if bookmarks need replay. Holding stale Range objects can retain detached nodes and increase memory. Caret correctness is a product cost, not a cosmetic detail: when it fails, users type into the wrong place. Measure edit latency and selection restoration failures across keyboard, touch, and assistive technologies.
Failure trace
The editor holds a Range pointing to a text node that was replaced when emphasis was applied. The next character appears at the start of the note. Another path maps only the start offset and reverses a backward selection. Test insert before selection, delete inside selection, split and join blocks, undo, remote insertion, block deletion, emoji and combining characters, and right-to-left text. Define offsets in one unit and do not mix UTF-16 DOM offsets with user-perceived grapheme counts without conversion.
Verification
- Caret survives a formatting render.
- Selection direction remains intact.
- Deleted blocks have a deterministic fallback.
Practice drill
Create a note with block 62 and a model selection from offsets 29 to 47. Apply emphasis, insert five code units before the range, and map both positions. Delete the block and verify the documented fallback. Repeat with a backward selection and an emoji before the caret. Inspect the browser selection after each render rather than assuming the model bookmark restored correctly.
Decision note
Map selections through model transactions and use DOM Range only as a temporary interface.
Common Mistakes
- Persisting a Range after its nodes are replaced.
- Mixing code-unit and grapheme offsets.
- Restoring only the start of a backward selection.
Connected lessons
Build Project: versioned case note editor and review Web Development: graphics and editor decisions quiz; follow Rich Document Editing and Safe Content; Editable Input, Composition, and Document Model; Paste Import, Sanitization, and Link Policy; Undo History, Autosave, and Revision Conflicts; Asynchronous Field Validation Races; Complex Accessible Interactions.
