← Back toMirror a text area

Highlight the current line in a text area

Written byPhuoc Nguyen
Created
27 Sep, 2023
If you're developing an application that requires users to input text into a text area, highlighting the current line number can make it easier for them to keep track of where they are in the document.
This feature is especially useful in code editors where developers spend a lot of time scrolling through and editing code. By highlighting the current line, they can stay oriented and avoid making changes to the wrong line.
Highlighting the current line is also beneficial in chat applications where users are sending messages back and forth. It helps users see which message they are responding to or editing, making the overall experience more intuitive and less confusing.
In this post, we'll show you how to implement this feature using JavaScript.

Creating the layout

In the previous post, we learned how to calculate the coordinates of the cursor in the text area. Now, let's use that knowledge to highlight the current line.
To do this, we'll create a div element that will be positioned absolutely within a mirrored element. The mirrored element will have the same styles and dimensions as the text area, while the highlighted element will have the same height as a single line of text and the same width as the text area.
To better visualize this, imagine the layout as follows:
html
<div class="container" id="container">
<div class="container__overlay">
<div class="container__highlight"></div>
<div class="container__mirror"></div>
</div>
<textarea id="textarea" class="container__textarea"></textarea>
</div>
The highlighted and mirrored elements are contained within a div with the class `container__overlay`. The overlay acts as a mirror for the text area, while the `container__mirror` element actually syncs with the text area's content.
The highlighted element is positioned absolutely within the overlay div, using the `position` style property. To ensure it takes up the full width of the text area, we set its `width` property to 100%.
css
.container__highlight {
position: absolute;
width: 100%;
}

Building the elements

Let's prepare the layout by dynamically creating and adding the necessary elements to the container. We'll use the common DOM APIs, so nothing too fancy here.
Here's a sample code:
js
const containerEle = document.getElementById('container');
const textarea = document.getElementById('textarea');

const overlayEle = document.createElement('div');
overlayEle.classList.add('container__overlay');
containerEle.prepend(overlayEle);

const highlightEle = document.createElement('div');
highlightEle.classList.add('container__highlight');
overlayEle.appendChild(highlightEle);

const mirroredEle = document.createElement('div');
mirroredEle.textContent = textarea.value;
mirroredEle.classList.add('container__mirror');
overlayEle.appendChild(mirroredEle);
What we did here was create the overlay element and added it to the container. Then, we created the highlighted and mirrored elements and added them to the overlay in the desired order.

Determining the position of the highlighted element

In our previous post, we introduced a method to retrieve the coordinates of the current cursor position within a text area. By splitting the content into two parts, we can identify the location of the cursor within the text.
To highlight the cursor position, we divide the mirrored element into three parts: two text nodes and an empty element in between. The text nodes represent the content before and after the cursor, while the empty element represents the cursor itself. Here's a sample code to illustrate what we're aiming for:
js
const cursorPos = textarea.selectionStart;
const textBeforeCursor = textarea.value.substring(0, cursorPos);
const textAfterCursor = textarea.value.substring(cursorPos);

const pre = document.createTextNode(textBeforeCursor);
const post = document.createTextNode(textAfterCursor);
const caretEle = document.createElement('span');
caretEle.innerHTML = '&nbsp;';

mirroredEle.innerHTML = '';
mirroredEle.append(pre, caretEle, post);
Now, to determine the position of the highlighted element, we can calculate it based on the cursor position. We use the `getBoundingClientRect()` function to get the rectangle of the cursor, which contains the `height` and `top` properties.
It's important to note that we need to add the `scrollTop` of the text area to the `top` property to ensure that the highlighted element stays in the correct position even when you scroll up or down within the text area. Here's a sample code to help you visualize how this works:
js
const rect = caretEle.getBoundingClientRect();
highlightEle.style.height = `${rect.height}px`;
highlightEle.style.top = `${rect.top + textarea.scrollTop}px`;
To give it a try, simply click the button at the bottom. But before you do, make sure to focus on the text area first.

Highlighting text as users move the cursor

When users move the cursor inside a text area, there's no built-in event to detect it. But don't worry, we can use the `selectionchange` event triggered when users select a text on the page. This event is also triggered when users focus on a text area or move the cursor inside it.
To highlight the current line, we handle the `selectionchange` event and check whether the text area is being focused by comparing the current active element (`document.activeElement`) and the text area. If they match, then we highlight the current line.
Here's an example code snippet to give you an idea of how it works:
js
const handleSelectionChange = () => {
if (document.activeElement === textarea) {
// Highlight the current line ...
}
};

document.addEventListener('selectionchange', handleSelectionChange);
In this demo, you can click inside the text area and move the cursor up or down using the up and down arrow keys. As you move the cursor, the target line will be highlighted. Try it out and see for yourself!
In reality, if we can detect that the cursor has moved within the same line as its previous position, then we may not need to make any calculations. This means we wouldn't have to update the mirror element or the highlighted element position. However, I'm leaving this task up to you.
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn