Web APIs - Selection and Range

Image of Author
March 30, 2022 (last updated September 21, 2022)

Selection and Range have historically confused me because I find their naming conventions unintuitive. I think there is good reason for the names, however, because the functionality they encompass is so robust. But it is still unfortunate that to isolate a point on a page you need a "selection containing a collapsed range".

Prerequisite: Everything is a Node

A Node is one of the most generic, abstracted, DOM interfaces. Every HTML element is a Node. Things that aren't elements, but still live on the page, like Text nodes inside html elements, or comments, are all inheriting the Node interface.

Keep in mind here that the DOM is the programmatic representation of the page, and was generated by parsing the HTML string that came from an HTTP request. You might think you are looking "at the HTML" when you are in the dev tools, but really you are looking at HTML Element objects inside the DOM object. My personal, historical confusion with the DOM stemmed from the false assumption that the DOM was somewhere hidden from me, doing all the magic stuff that made the browser work. I assumed there was some mystery I didn't understand. But there's no mystery, I just need to keep in mind that really, there's "no HTML", there's just the DOM. When I change the HTML string, and then give it to the browser, the browser parses it and boom, the DOM again, staring me in the face.

Selection

A selection is a part of an HTML page that a user has "selected" for interaction.When a user highlights text, that's a selection. When a user clicks into an input or contenteditable element, that's a selection. In my mind I might read an html page and say to myself, "I like this sentence and want to copy it to my system clipboard and paste it into my own personal notes." Well, the browsers need to support that functionality, and they do so by allowing users to create Selection objects when interacting with the page.

Another way to build intuition around the word "selection" is to recall that dropdowns in html are actually select elements. The user is selecting something. If it's an option from a list of options in a dropdown, it's with the select element. If it's a part of the html page as a whole, it's with the selection object.

What a user selects is usually just one contiguous chunk of the page, e.g., a single sentence. But, Selection objects also let you select mutliple, non-contiguous parts of a page. It does this by creating sub-selection objects, each of which belong to a single Selection object. Simple Selections contain just one of these sub-selection objects, while complex selection contain arbitrarily many. The name for these sub-selection objects is Range.

Range

A Range is a necessarily continguous part of an HTML page. It's relationship to Selection is confusing because people are mostly intending their selection to be just one, contiguous part of an HTML page. In the land of Web APIs, what that turns in to is a selection containing a single range. So, the takeaway from this conceptual split between ranges and selections is that you will need to use range apis to manipulate what specifically you are selecting, and then you will need to use selection apis to bring all the ranges together and "complete" the selection by interacting with the page itself. It's an abstract, two-layer interaction, and you neeed to understand them both to achieve your ends as a web dev.

A point on the page

Given an input element or contenteditable element, a web dev might desire control over where the caret, aka text cursor, aka blinking line representing where the next character will be inerted... a web dev might desire control over where the caret will be. This is a programmatic (as opposed to user-interaction-triggered) selection.

To being with, you need a range that represent a single point, and then you will put that range in a selection, and put that selection on the page in the appropraite place.

Create a range. Specifying a point on a page is a special case for the range object. Normally, a range spans across a range of nodes, or parts of nodes.

const range = document.createRange();