The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Create a collapsed range | const range = document.createRange() | View examples |
| Set text boundaries | range.setStart(textNode, 2); range.setEnd(textNode, 7) | View examples |
| Select a node | range.selectNode(element) | View examples |
| Select node contents | range.selectNodeContents(element) | View examples |
| Read document selection | const selection = document.getSelection() | View examples |
| Guard an empty selection | if (selection && selection.rangeCount > 0) selection.getRangeAt(0) | View examples |
| Display a range | selection.removeAllRanges(); selection.addRange(range) | View examples |
| Observe selection changes | document.addEventListener('selectionchange', updateToolbar) | View examples |
| Clone selected content | const fragment = range.cloneContents() | View examples |
| Move selected content out | const fragment = range.extractContents() | View examples |
| Insert at a boundary | range.insertNode(document.createTextNode('Note')) | View examples |
| Measure line fragments | const rects = range.getClientRects() | View examples |
| Compare two starts | a.compareBoundaryPoints(Range.START_TO_START, b) | View examples |
| Read textarea offsets | const selected = field.value.slice(field.selectionStart, field.selectionEnd) | View examples |
| Map across shadow roots | selection.getComposedRanges({ shadowRoots: [root] }) | View examples |
A Range represents contiguous DOM content between two boundary points; a Selection represents what the user or script has selected in a document. They are related but not interchangeable. Correct code accounts for text-versus-element offsets, empty selections, live boundary adjustment during DOM mutation, user selection direction, form-control-specific APIs, geometry containing multiple fragments, and shadow-tree boundaries.
Step by step
Detailed examples
Place boundary points using the correct offset model
A boundary point is a node plus an offset. In Text, Comment, and other CharacterData nodes, the offset counts UTF-16 code units; in an Element or DocumentFragment it counts child nodes. setStart and setEnd can collapse or reorder the range according to the DOM algorithms. selectNode includes a node, whereas selectNodeContents includes only its descendants.
<p id="message">Read the documentation.</p>
<script>
const text = message.firstChild;
const start = text.data.indexOf('documentation');
const range = document.createRange();
range.setStart(text, start);
range.setEnd(text, start + 'documentation'.length);
console.log(range.toString()); // documentation
</script> Treat Selection as user-visible document state
Document.getSelection returns the document Selection, which can be empty. getRangeAt throws when its index is unavailable, so check rangeCount first. removeAllRanges followed by addRange visibly replaces the selection and may move the caret. Listen for selectionchange on document, and debounce expensive toolbar or annotation work because selection can change frequently while the user drags or types.
<p id="result">Build complete: 42 checks passed.</p>
<button id="select-result" type="button">Select result</button>
<script>
selectResult.addEventListener('click', () => {
const selection = document.getSelection();
if (!selection) return;
const range = document.createRange();
range.selectNodeContents(result);
selection.removeAllRanges();
selection.addRange(range);
});
</script> Clone, extract, delete, and insert with DOM semantics
cloneContents copies the selected subtree into a DocumentFragment; extractContents removes and returns it; deleteContents removes it without returning a fragment. Partial element selection clones the ancestor structure needed to contain partial descendants, and cloned id attributes can create duplicates when inserted into the same document. insertNode inserts at the start and can split a Text node. Sanitize untrusted fragments before reinsertion.
<p id="sentence">Ship on Thursday afternoon.</p>
<script>
const text = sentence.firstChild;
const range = document.createRange();
range.setStart(text, 8);
range.setEnd(text, 16);
range.deleteContents();
const replacement = document.createTextNode('Friday');
range.insertNode(replacement);
range.setStartAfter(replacement);
range.collapse(true);
</script> Expect ranges to occupy multiple visual fragments
Logical DOM order and visual layout are different, particularly for wrapped or bidirectional text. getClientRects returns the rendered fragments and getBoundingClientRect returns their enclosing rectangle. Empty or non-rendered content may yield empty or zero-sized results. compareBoundaryPoints compares DOM boundary order, not screen coordinates, and requires ranges whose roots are compatible.
<p id="copy">Select part of this paragraph after it wraps across several lines.</p>
<div id="toolbar" hidden style="position: fixed">Selection tools</div>
<script>
document.addEventListener('selectionchange', () => {
const selection = document.getSelection();
if (!selection || selection.isCollapsed || !selection.rangeCount) { toolbar.hidden = true; return; }
const rect = selection.getRangeAt(0).getBoundingClientRect();
toolbar.hidden = rect.width === 0 && rect.height === 0;
toolbar.style.transform = `translate(${rect.left}px, ${rect.top}px)`;
});
</script> Use specialized APIs for controls and composed trees
The internal text selection of input and textarea controls is represented by selectionStart, selectionEnd, selectionDirection, and setSelectionRange rather than the document Selection's DOM range. A Selection crossing shadow boundaries requires extra care: getComposedRanges can return StaticRange objects mapped through explicitly supplied shadow roots, but feature-detect it and design a fallback because support varies.
<textarea id="field">release candidate</textarea>
<button id="uppercase" type="button">Uppercase selection</button>
<script>
uppercase.addEventListener('click', () => {
const start = field.selectionStart;
const end = field.selectionEnd;
const replacement = field.value.slice(start, end).toUpperCase();
field.setRangeText(replacement, start, end, 'select');
field.focus();
});
</script> Local code tester
Create, display, and measure a DOM range
Select the emphasized phrase programmatically and inspect its text and number of rendered rectangles.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



