W3docs

JavaScript Selection and Range API

JavaScript is a powerful tool for web developers, and understanding how to manipulate the document object model (DOM) is crucial for creating dynamic web

Whenever a user drags the cursor across text on a page, the browser tracks what was selected. JavaScript exposes that information — and lets you create selections yourself — through two related DOM interfaces:

  • A Range is a pair of boundary points (a start and an end) inside the document. It describes which part of the document you mean, down to a specific character offset inside a text node. A range can exist purely in memory without the user being aware of it.
  • A Selection is what the user currently has highlighted. It is essentially a wrapper around one or more ranges, tied to the on-screen highlight and the caret (text cursor).

You reach for these APIs when you need to build a rich-text editor, a "highlight and comment" feature, a custom find-and-replace, or anything that programmatically reads, moves, or styles selected text. This chapter covers building ranges, reading and changing the user's selection, and putting the two together to highlight and insert content.

This builds on how the DOM is structured. If node types and offsets are new to you, read Node properties: type, tag and contents and Modifying the document first.

Understanding the Selection Interface

The Selection interface represents the text the user has highlighted, or the current position of the caret when nothing is highlighted. You access it with the global window.getSelection() method (often just getSelection()). Internally a selection holds zero or more Range objects; in practice most browsers only ever keep a single range, so getRangeAt(0) is the common way to read it.

The quickest way to see what is selected is toString(), which returns the selected text as a plain string:

// After the user highlights something on the page:
const selectedText = window.getSelection().toString();
console.log(selectedText); // whatever the user highlighted

Useful properties and methods of Selection

  • rangeCount: The number of ranges in the selection — 0 when nothing is selected. Always check this before calling getRangeAt.
  • toString(): Returns the selected text as a string.
  • getRangeAt(index): Returns the Range at the given index (use 0 for the current selection).
  • addRange(range): Adds a Range to the selection, highlighting it on screen.
  • removeAllRanges(): Clears the selection entirely.
  • removeRange(range): Removes one specific range. Most browsers only keep a single range, so removeAllRanges() is the practical choice.
  • collapse(node, offset): Collapses the selection to a single point (an empty caret) inside node.

A common pattern is to read the current selection, modify it, then write a new selection back — removeAllRanges() followed by addRange().

Practical example: highlighting text

To highlight what the user selected, take its range, pull the selected nodes out of the document, wrap them in a styled <span>, and put that span back where the content was.

extractContents() removes the selected content from the DOM and returns it as a document fragment, leaving the range empty (collapsed) at that spot — which is exactly where we then insert the span.

<div id="text">Select some of this text and press the button.</div>
<button onclick="highlightText()">Highlight</button>

<script>
function highlightText() {
  const selection = window.getSelection();
  if (!selection.rangeCount) return false;
  const range = selection.getRangeAt(0);
  const span = document.createElement('span');
  span.style.backgroundColor = 'yellow';
  const fragment = range.extractContents();
  span.appendChild(fragment);
  range.insertNode(span);
}
</script>

Exploring the Range Interface

A Range marks a fragment of the document with two boundary points — a start and an end — each defined by a node and an offset. The meaning of the offset depends on the node:

  • Inside a text node, the offset is a character index (e.g. offset 5 is between the 5th and 6th character).
  • Inside an element node, the offset counts child nodes (e.g. offset 0 is before the first child).

Create an empty range with document.createRange(), then position its boundaries. You can find the nodes to point at with getElementById / querySelector.

Setting the boundaries

const p = document.querySelector('p');
const textNode = p.firstChild;        // the text inside <p>

const range = document.createRange();
range.setStart(textNode, 0);          // start at the first character
range.setEnd(textNode, 5);            // end before the 6th character
console.log(range.toString());        // first 5 characters of the paragraph

Two shortcuts cover the most common cases so you do not have to compute offsets:

  • selectNode(node) — the range spans the node and its surrounding tags.
  • selectNodeContents(node) — the range spans only what is inside the node.

Useful methods of Range

  • setStart(node, offset) / setEnd(node, offset): Position the start and end boundaries.
  • selectNode(node) / selectNodeContents(node): Set both boundaries around a node or its contents.
  • toString(): The text inside the range.
  • cloneContents(): Returns a copy of the range's contents as a document fragment, leaving the document untouched.
  • extractContents(): Moves the contents out of the document into a fragment and returns it.
  • deleteContents(): Removes the range's contents and returns nothing.
  • cloneRange(): Returns a copy of the range object itself (not its contents).
  • insertNode(node): Inserts a node at the start of the range.
  • surroundContents(node): Wraps the range's contents inside node — handy for highlighting, but it throws if the range partially crosses a non-text element.

Remember the difference: cloneContents() copies, extractContents() moves out and returns, deleteContents() removes and discards.

Practical example: extracting text

This reads everything inside an element with a range and transforms the text without touching the original DOM:

<div id="content">This is some sample text for extraction.</div>
<button onclick="extractText()">Extract and Manipulate</button>

<script>
function extractText() {
  const range = document.createRange();
  const content = document.getElementById('content');
  range.selectNodeContents(content);
  const extractedText = range.toString();
  const manipulatedText = extractedText.replace('sample', 'example'); // Manipulating text
  alert(manipulatedText);
}
</script>

In the above example, the script replaces the word "sample" with "example" in the extracted text before showing it in an alert box. This is a basic manipulation but demonstrates how you can start to work with the text once extracted.

Advanced Text Operations

Beyond basic text manipulation, the Selection and Range interfaces allow for more complex operations like inserting nodes directly into the document.

Example: Inserting Text

This example uses a contenteditable div: the user clicks to place the caret, and the button inserts 'Hello World' at that exact spot. Note how deleteContents() first removes anything selected, then the new text node is inserted and re-selected so the caret lands after it.

<div id="editable" contenteditable="true" style="border: 1px solid #ccc; padding: 10px; min-height: 50px;">
  Click here and set the cursor position.
</div>
<button onclick="insertText()">Insert 'Hello World'</button>

<script>
function insertText() {
  const editableDiv = document.getElementById('editable');
  const sel = window.getSelection();
  
  // Check if the selection is within the editable div
  if (!sel.rangeCount || !editableDiv.contains(sel.getRangeAt(0).commonAncestorContainer)) return;
  
  const range = sel.getRangeAt(0);
  range.deleteContents();  // Clears any selected text

  const textNode = document.createTextNode('Hello World');
  range.insertNode(textNode);

  sel.removeAllRanges();   // Clear the previous selection
  sel.addRange(range);     // Re-select the new text node
}
</script>

Summary

  • A Range is two boundary points (node + offset) describing a fragment of the document; build one with document.createRange() and position it with setStart/setEnd or the selectNode/selectNodeContents shortcuts.
  • A Selection, from window.getSelection(), wraps the user's on-screen highlight. Read it with toString() and getRangeAt(0); rewrite it with removeAllRanges() + addRange().
  • For content: cloneContents() copies, extractContents() moves out, deleteContents() discards, and insertNode / surroundContents put nodes back.

Together these let you highlight, extract, and insert content precisely — the foundation of rich-text editors and annotation tools.

Keep going: Modifying the document · Node properties: type, tag and contents · Searching: getElement* and querySelector

Practice

Practice
Which of the following statements are true regarding the JavaScript Range and Selection interfaces?
Which of the following statements are true regarding the JavaScript Range and Selection interfaces?
Was this page helpful?