libs: - d3 - domtree --- # Selection and Range In this chapter we'll cover selection in the document, as well as selection in form fields, such as ``. JavaScript can access an existing selection, select/deselect DOM nodes as a whole or partially, remove the selected content from the document, wrap it into a tag, and so on. You can find some recipes for common tasks at the end of the chapter, in "Summary" section. Maybe that covers your current needs, but you'll get much more if you read the whole text. The underlying `Range` and `Selection` objects are easy to grasp, and then you'll need no recipes to make them do what you want. ## Range The basic concept of selection is [Range](https://dom.spec.whatwg.org/#ranges), that is essentially a pair of "boundary points": range start and range end. A `Range` object is created without parameters: ```js let range = new Range(); ``` Then we can set the selection boundaries using `range.setStart(node, offset)` and `range.setEnd(node, offset)`. The first argument `node` can be either a text node or an element node. The meaning of the second argument depends on that: - If `node` is a text node, then `offset` must be the position in the text. - If `node` is an element node, then `offset` must be the child number. For example, let's create a range in this fragment: ```html autorun
Example: italic and bold
``` Here's its DOM structure: Let's make a range for `"Example: italic"`. As we can see, this phrase consists of exactly the first and the second children of ``:  - The starting point has `
` as the parent `node`, and `0` as the offset. - The ending point also has `
` as the parent `node`, but `2` as the offset (it specifies the range up to, but not including `offset`). Here's the demo, if you run it, you can see that the text gets selected: ```html run
Example: italic and bold
``` Here's a more flexible test stand where you try more variants: ```html run autorunExample: italic and bold
From – To ``` E.g. selecting in the same `` from offset `1` to `4` gives range `italic and bold`:  We don't have to use the same node in `setStart` and `setEnd`. A range may span across many unrelated nodes. It's only important that the end is after the start. ### Selecting parts of text nodes Let's select the text partially, like this:  That's also possible, we just need to set the start and the end as a relative offset in text nodes. We need to create a range, that: - starts from position 2 in `
` first child (taking all but two first letters of "Example: ") - ends at the position 3 in `` first child (taking first three letters of "bold", but no more): ```html run
Example: italic and bold
``` The range object has following properties:  - `startContainer`, `startOffset` -- node and offset of the start, - in the example above: first text node inside `` and `2`. - `endContainer`, `endOffset` -- node and offset of the end, - in the example above: first text node inside `` and `3`. - `collapsed` -- boolean, `true` if the range starts and ends on the same point (so there's no content inside the range), - in the example above: `false` - `commonAncestorContainer` -- the nearest common ancestor of all nodes within the range, - in the example above: `
` ## Range methods There are many convenience methods to manipulate ranges. Set range start: - `setStart(node, offset)` set start at: position `offset` in `node` - `setStartBefore(node)` set start at: right before `node` - `setStartAfter(node)` set start at: right after `node` Set range end (similar methods): - `setEnd(node, offset)` set end at: position `offset` in `node` - `setEndBefore(node)` set end at: right before `node` - `setEndAfter(node)` set end at: right after `node` **As it was demonstrated, `node` can be both a text or element node: for text nodes `offset` skips that many of characters, while for element nodes that many child nodes.** Others: - `selectNode(node)` set range to select the whole `node` - `selectNodeContents(node)` set range to select the whole `node` contents - `collapse(toStart)` if `toStart=true` set end=start, otherwise set start=end, thus collapsing the range - `cloneRange()` creates a new range with the same start/end To manipulate the content within the range: - `deleteContents()` -- remove range content from the document - `extractContents()` -- remove range content from the document and return as [DocumentFragment](info:modifying-document#document-fragment) - `cloneContents()` -- clone range content and return as [DocumentFragment](info:modifying-document#document-fragment) - `insertNode(node)` -- insert `node` into the document at the beginning of the range - `surroundContents(node)` -- wrap `node` around range content. For this to work, the range must contain both opening and closing tags for all elements inside it: no partial ranges like `abc`. With these methods we can do basically anything with selected nodes. Here's the test stand to see them in action: ```html run refresh autorun height=260 Click buttons to run methods on the selection, "resetExample" to reset it.
Example: italic and bold
``` There also exist methods to compare ranges, but these are rarely used. When you need them, please refer to the [spec](https://dom.spec.whatwg.org/#interface-range) or [MDN manual](mdn:/api/Range). ## Selection `Range` is a generic object for managing selection ranges. We may create `Range` objects, pass them around -- they do not visually select anything on their own. The document selection is represented by `Selection` object, that can be obtained as `window.getSelection()` or `document.getSelection()`. A selection may include zero or more ranges. At least, the [Selection API specification](https://www.w3.org/TR/selection-api/) says so. In practice though, only Firefox allows to select multiple ranges in the document by using `key:Ctrl+click` (`key:Cmd+click` for Mac). Here's a screenshot of a selection with 3 ranges, made in Firefox:  Other browsers support at maximum 1 range. As we'll see, some of `Selection` methods imply that there may be many ranges, but again, in all browsers except Firefox, there's at maximum 1. ## Selection properties Similar to a range, a selection has a start, called "anchor", and the end, called "focus". The main selection properties are: - `anchorNode` -- the node where the selection starts, - `anchorOffset` -- the offset in `anchorNode` where the selection starts, - `focusNode` -- the node where the selection ends, - `focusOffset` -- the offset in `focusNode` where the selection ends, - `isCollapsed` -- `true` if selection selects nothing (empty range), or doesn't exist. - `rangeCount` -- count of ranges in the selection, maximum `1` in all browsers except Firefox. ````smart header="Usually, the selection end `focusNode` is after its start `anchorNode`, but it's not always the case" There are many ways to select the content, depending on the user agent: mouse, hotkeys, taps on a mobile etc. Some of them, such as a mouse, allow the same selection can be created in two directions: "left-to-right" and "right-to-left". If the start (anchor) of the selection goes in the document before the end (focus), this selection is said to have "forward" direction. E.g. if the user starts selecting with mouse and goes from "Example" to "italic":  Otherwise, if they go from the end of "italic" to "Example", the selection is directed "backward", its focus will be before the anchor:  That's different from `Range` objects that are always directed forward: the range start can't be after its end. ```` ## Selection events There are events on to keep track of selection: - `elem.onselectstart` -- when a selection starts on `elem`, e.g. the user starts moving mouse with pressed button. - Preventing the default action makes the selection not start. - `document.onselectionchange` -- whenever a selection changes. - Please note: this handler can be set only on `document`. ### Selection tracking demo Here's a small demo that shows selection boundaries dynamically as it changes: ```html run height=80Select me: italic and bold
From – To ``` ### Selection getting demo To get the whole selection: - As text: just call `document.getSelection().toString()`. - As DOM nodes: get the underlying ranges and call their `cloneContents()` method (only first range if we don't support Firefox multiselection). And here's the demo of getting the selection both as text and as DOM nodes: ```html run height=100Select me: italic and bold
Cloned:`: ```html run
Select me: italic and bold
``` The same thing using ranges: ```html runSelect me: italic and bold
``` ```smart header="To select, remove the existing selection first" If the selection already exists, empty it first with `removeAllRanges()`. And then add ranges. Otherwise, all browsers except Firefox ignore new ranges. The exception is some selection methods, that replace the existing selection, like `setBaseAndExtent`. ``` ## Selection in form controls Form elements, such as `input` and `textarea` provide [special API for selection](https://html.spec.whatwg.org/#textFieldSelection), without `Selection` or `Range` objects. As an input value is a pure text, not HTML, there's no need for such objects, everything's much simpler. Properties: - `input.selectionStart` -- position of selection start (writeable), - `input.selectionEnd` -- position of selection end (writeable), - `input.selectionDirection` -- selection direction, one of: "forward", "backward" or "none" (if e.g. selected with a double mouse click), Events: - `input.onselect` -- triggers when something is selected. Methods: - `input.select()` -- selects everything in the text control (can be `textarea` instead of `input`), - `input.setSelectionRange(start, end, [direction])` -- change the selection to span from position `start` till `end`, in the given direction (optional). - `input.setRangeText(replacement, [start], [end], [selectionMode])` -- replace a range of text with the new text. Optional arguments `start` and `end`, if provided, set the range start and end, otherwise user selection is used. The last argument, `selectionMode`, determines how the selection will be set after the text has been replaced. The possible values are: - `"select"` -- the newly inserted text will be selected. - `"start"` -- the selection range collapses just before the inserted text (the cursor will be immediately before it). - `"end"` -- the selection range collapses just after the inserted text (the cursor will be right after it). - `"preserve"` -- attempts to preserve the selection. This is the default. Now let's see these methods in action. ### Example: tracking selection For example, this code uses `onselect` event to track selection: ```html run autorun