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:
<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%.
.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:
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:
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 = ' ';
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:
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:
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.
It's highly recommended that you visit the original post to play with the interactive demos.
If you found this series 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!
If you want more helpful content like this, feel free to follow me:
Top comments (0)