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
Rangeis 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
Selectionis 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 highlightedUseful properties and methods of Selection
rangeCount: The number of ranges in the selection —0when nothing is selected. Always check this before callinggetRangeAt.toString(): Returns the selected text as a string.getRangeAt(index): Returns theRangeat the given index (use0for the current selection).addRange(range): Adds aRangeto the selection, highlighting it on screen.removeAllRanges(): Clears the selection entirely.removeRange(range): Removes one specific range. Most browsers only keep a single range, soremoveAllRanges()is the practical choice.collapse(node, offset): Collapses the selection to a single point (an empty caret) insidenode.
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
5is between the 5th and 6th character). - Inside an element node, the offset counts child nodes (e.g. offset
0is 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 paragraphTwo 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 insidenode— 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
Rangeis two boundary points (node + offset) describing a fragment of the document; build one withdocument.createRange()and position it withsetStart/setEndor theselectNode/selectNodeContentsshortcuts. - A
Selection, fromwindow.getSelection(), wraps the user's on-screen highlight. Read it withtoString()andgetRangeAt(0); rewrite it withremoveAllRanges()+addRange(). - For content:
cloneContents()copies,extractContents()moves out,deleteContents()discards, andinsertNode/surroundContentsput 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