The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Create a collapsed rangeconst range = document.createRange()View examples
Set text boundariesrange.setStart(textNode, 2); range.setEnd(textNode, 7)View examples
Select a noderange.selectNode(element)View examples
Select node contentsrange.selectNodeContents(element)View examples
Read document selectionconst selection = document.getSelection()View examples
Guard an empty selectionif (selection && selection.rangeCount > 0) selection.getRangeAt(0)View examples
Display a rangeselection.removeAllRanges(); selection.addRange(range)View examples
Observe selection changesdocument.addEventListener('selectionchange', updateToolbar)View examples
Clone selected contentconst fragment = range.cloneContents()View examples
Move selected content outconst fragment = range.extractContents()View examples
Insert at a boundaryrange.insertNode(document.createTextNode('Note'))View examples
Measure line fragmentsconst rects = range.getClientRects()View examples
Compare two startsa.compareBoundaryPoints(Range.START_TO_START, b)View examples
Read textarea offsetsconst selected = field.value.slice(field.selectionStart, field.selectionEnd)View examples
Map across shadow rootsselection.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

01

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.

Select one word inside a Text node
<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>
Back to quick reference ↑
02

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.

Select a result when the user requests it
<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>
Back to quick reference ↑
03

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.

Replace selected contents with a Text node
<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>
Back to quick reference ↑
04

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.

Anchor a toolbar above the selected fragments
<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>
Back to quick reference ↑
05

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.

Replace selected text in a textarea
<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>
Back to quick reference ↑

Local code tester

Create, display, and measure a DOM range

Select the emphasized phrase programmatically and inspect its text and number of rendered rectangles.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumSelection APIw3.org
  2. WHATWGDocument Object Model Standard: Rangesdom.spec.whatwg.org
  3. WHATWGHTML Standard: Text control selectionshtml.spec.whatwg.org
  4. World Wide Web ConsortiumCSSOM View Module: Range extensionsw3.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback