How to Work With “Selection” & “Cursor”

Sang Nguyen
JavaScript in Plain English
11 min readJul 14, 2022

--

A tutorial on “selection” and “cursor”

Photo by Patrick Fore on Unsplash

In web development, sometimes we will deal with “selection” and “cursor”. Maybe it’s about highlighting the text, controlling the position of the cursor, or something more advanced like dealing with editor… I’ve joined a project about an editor that needs work with the selection. I will discuss it in this article.

What are “selection” and “cursor”?

Here is “selection” — text or element is selected.

Selection

And “cursor” is a blinking vertical line (or horizontal line) in the input area.

Cursor

But it is just a definition from the user’s point of view, from the developer’s perspective, we have two important objects that have to be mentioned: Selection and Range. These are two objects that have many properties and methods. To read more about it, you can check the official documentation. Here, I’m quoting a short introduction for them:

  1. The “selection” object represents the text range selected by the user or the current position of the caret. It represents a selection of text on the page, which may span multiple elements. Usually generated by the user dragging the mouse over text.
  2. Range objects represent document fragments containing nodes and partial text nodes. The selection object obtained through the range an object is the focus of our operation of the cursor.

To get object selection, we can use the method getSelection from window. Here is an example of it:

window.getSelection();
Object selection from window

In a real case, I don’t often work directly with the object selection . Because I ofter work with range which selected by the user. To get that range we just need to call a simple code:

const selection = window.getSelection();
selection.getRangeAt(0);
Object from range

In the code above, we need getRangeAt with a number because in some browsers (Firefox) users can select multiple texts. With selection and range , we have two main objects with which we will work with are normal input (input, textarea) and editable element. In the next part, I will make some examples with them.

Normal input (Input and Textarea)

In this part, we will work with input and textarea by some simple examples. Because they are almost the same so I will make examples with textarea. To start our examples, we need to create a short HTML with textarea with ID. In my code, I set that id as “textbox”. It is just simple like this:

<textarea id="textbox">One Two Three</textarea>

In the next examples, I will make some functions to run it. You can run this function in the console or bind it to the button… anyway you feed well.

Select a range

The first example is a simple activity, select a text. To select a text in the input, we have a method setSelectionRange from HMTLInputElement. If you want to use it, just need to call it from HTMLInputElement with two arguments are positionStart and positionEnd . There are position start and position end of the area you want to select any value of this base on an index of the character in input. Here is an example code for it with the ID of textarea is “textbox”:

function selectText() {
const textbox = document.getElementById('textbox');
textbox.focus();
textbox.setSelectionRange(0, 3);
}

Here is the result I got with this function:

You can see that the three first characters (One) are selected. If you want to select all of the text, you can call the method select like this textbox.select(). In case you just want to put the cursor in a position, let call setSelectionRange with postionStart and positionEnd is a position you want to put the cursor. Here is my result for them:

Put cursor position
Select all text of input

Get position start and position end

Above are some ways to modify a selection of input. If you want to get the current position start and end of selection in some input, you can try with property selectionStart and selectionEnd . It will help you get that values.

Modify the text which is selected

After working with the selected range, we will move to an important method to replace the text of the selected range. To do that, we can use the method setRangeText . If you just want to replace the selected text with another text, you just need to call that method with only one parameter the words you want to replace. If you want to replace text by position start and end, you just need to fill two more parameters position start and end. Here are two examples of functions for replacing selected text and replacing text by position:

Here is the result for two of these functions:

Replace selected text
Replace by postion

There are some basic examples to play with selection for HTMLInputElement. Here is my full code for it after being modified a bit with the flexible elements.

There are some simple examples I want to share with you about how to work with selection in normal input. In the next part, we will work with content-editable elements.

Content-editable element

Before starting work with it, I think we need to know about the content-editable element. Did you use any editor before? Many editors build with the content editable elements. To make a content editable element we just need to create an HTML element (div, span…) and enable attribute contenteditable="true" to the element, or the CSS attribute -webkit-user-modify.

<div contenteditable="true" id="editor">Here is content editable element</div>

Or with CSS:

div {
-webkit-user-modify: read-write;
}

It looks like a normal input:

If it looks like with normal a normal input, why do we need it? So simple, with content-editable elements, we can make more advanced things from bold, italic, and underline… to variables, links, and tables. Many things! In the above, I’ve mentioned getSelection and range. But we have worked with a method from HTMLInputElement instead of getSelection and range. Because it is a good way to work with HTMLInputElement. We will use them for content-editable elements. Let’s go.

Selected an area

Like the previous part, we will start by working with a selected area. Because the selection of content-editable elements is much more complicated than the form, so we need more than one step to select an area. With normal input, we just need to position the start and position the end of the selection. But with the content-editable element, we need two steps to make a range and apply it to selection. Here is just a simple example of it:

range.setStart(startNode, startOffset);
range.setEnd(endtNode, endOffset);
selection.removeAllRanges();
selection.addRange(range);

Because an element can have many nodes inside. That’s why we need two steps to set start and end positions with the start and end nodes. Before starting with an element that has multiple child nodes, I think we should make a simple example with an element have one child node. Just use the div tag we have created above. Here is the JavaScript code to select an area with start and end positions:

Let’s try to use this function to select “content” in the element. Here is the result:

It works. In this example, I used firstChild for setStart and setEnd because our element only has one child node. Now we will move to an element have many child nodes aka “Rich text”.

Selected an area in rich text

I think we need to update a bit for example our div editor like this:

<div id="editor" contenteditable="true" id="editor">Hello, my name is <b>Tasy</b>. And you?</div>

I think understanding about DOM structure of this element is a good first step to start. Firstly, we can use property childNodes to get child nodes of this element:

Based on the result, we can see this element have three child nodes. We can see an image below to understand it easier:

Let’s make a simple example. Select a text containing “my name is Tasy”. We have three child nodes in this element (text, bold, text). “my name is” in the first node. “Tasy” in the second node. Based on the previous example, we can see that our range can start in the first node with position 6 and end in the second node with position 3. Here is the code to do that:

Let’s test it!

We got this error. Because we don’t have a child at offset 4 in the second child node. Let's try to replace the second node with its #text element.

Thanks, Lord. It works! It means the second parameter in the method setEnd and setStart is an offset of a child. If we want to access the content inside of some element, we should work with children of this element. I’ll draw a simple tree below. I hope it will help you understand our example easier.

Following this structure, I think you can find your way to select any area you want.

Remove and replace the text which is selected

This is the last part of this article. I will show you one of the ways to modify selected content. To do that, we need to work with Range’s methods. The first method is deleteContents which allows us to remove selected content. Let's try with it by this code:

const range = document.getSelection().getRangeAt(0);
range.deleteContents();

Here is the result:

A selected text has been removed. It’s very easy. In the next example, I’ll show you the way to replace selected content with other content. Before starting to do this, I think we can note that Range’s content is almost the same as Node content. It means we can insert a node into Range’s content. If you want to insert new content after selected content, you can use a method insertNode with Node as a parameter. Now, let’s try to replace a selected content in this example:

const range = document.getSelection().getRangeAt(0);
const newText = document.createTextNode('I am');
range.deleteContents();
range.insertNode(newText);

In this example, I replaced “my name is” with “I am” with the source code above. Let’s see a result.

Let's make one more example for the case replace multiple elements in the selection.

It still works. It means we can apply this way for both cases. Besides that, I think we need to know a little thing before moving to the next part. In the first example, when we replaced “my name is” with “I am”, It wasn’t only replaced content, It also made a new text element. I made a photo to compare child nodes before and after running our code:

Let’s remember it to avoid unexpected problems. Let’s go to the next part.

Wrap the text which is selected

In the above examples, we have learned the ways to replace selected content. In this part, we will learn an important thing. Did you make a text become bolding, italicizing, or change its color in an editor? In this part, we will learn a way to do it. Based on things we did in the previous part about how to replace a selected text. We just need to learn one more thing the way to get selected content. Then put this content into an element we want to insert in the Range after removed old content. Here to an example code to change a selected content color to green:

We can see our result here:

The selected text color has been changed to green. Of course, we not only change text color, but we also created a new element (like the examples above). Here are the child nodes after running our code:

We can see that one more node was created and added to the child node list. In this node, we can see two child nodes are text and b which were selected before. And I think can end this part with this note. It is also the last part about how to work with the content editable element.

Conclusion

In the past, I had a chance to work on a project in my former company. In this project, we aim to build an editor with many functions. With a weak process development, we chose and switched many libraries to build this editor. Every time got a problem, we only can search it on Google and hope to find the solution. I think I could do it better if I spent my time learning about selection and range.

So I think I can write a simple article about how to work with selection and range. I hope this article is helpful for you. Read more for Selection and Range here:

Photo by Anders Nord on Unsplash

Thank you for reading.

Connect with me via Linkedin or Twitter.

More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter and LinkedIn. Check out our Community Discord and join our Talent Collective.

--

--