HOW TO: Where’s the text cursor? 🧐
Finding the text input cursor position with JavaScript
A rather specific scenario here. But, how do you get the location of the cursor in a textarea
? or an input
? There is no straightforward method or property that can you can utilise. You can get selection indexes but these don’t give you the actual cursor location.
For those in camp TL;DR; do some trickery 🐇 by cloning the input and getting the position of the appended span
. Here’s a demo! 🎉
Why?
Use cases where you need to show some custom UI at the exact location where the user is interacting. Popular examples include type aheads such as those for Github issues and Twitter hashtags. Or interactive tooltips likes those on the Medium WYSIWYG editor.
How?
The concept is actually pretty simple.
- Create a replica element of our
textarea
/input
with near identical styling. - Generate a substring of the
textarea
/input
text content up to the point of the caret. - Populate the replica element with that substring.
- Append a
span
element that contains the rest of the content. - Append the replica element to the
DOM
The position of the cursor is the position of the appended span
💡 Once we have the position of the span
, we need to remove the replica element from the DOM
. Note, the position of the span will be relative to the replica element. To get the absolute positioning on the page of the cursor we will also need to account for the location of the input
/textarea
. This is critical so that we don’t have to start wrapping our input
elements and allows us to append our custom UI to the document.body
👍
This is the foundation for finding the cursor position. We can refer to it as the foundation cursor position. It does not consider potential scroll position, custom styling or our desired behavior.
Those calculations are best made to the obtained foundation cursor position.
For example, you may want to show custom UI beneath the cursor or center aligned above the cursor. In both use cases, you will need the foundation cursor position before anything else. We will look at different implementations below.
Code
So how does that code look for obtaining the foundation cursor position? 🤓
Pretty much like that 😉
So what’s happening there?
We’ve gone over the basic concept in “How?” but let’s walk through what’s happening;
- Pass the
input
/textarea
along with the selection index as params to ourgetCursorXY
method - Create a replica
div
and copy the styling of ourinput
by looping over the returnedObject
fromgetComputedStyle
- Fill that replica
div
with a substring of text content up until the selection index - Create a
span
element and give it the text content of the remaining text content not included in the substring - Append that
span
to the replicadiv
- Append that replica
div
to theDOM
- Obtain the location of the cursor 🎉
- Remove the replica element from the
DOM
- Return the text cursor position accounting for the
input
position on the page 👍
Real scenarios
Cool, but how can we use this in real scenarios and how does the HTML
work, can I just use this on an a plain input
/textarea
? 🤔
Yes! You can. Using the getCursorXY
method against your input
will give you the correct cursor location. It is then up to you what you do with the location.
Showing the cursor position on input and click
Let’s walk through one of the demo scenarios.
The desired behavior is that whenever we type or click, there will be an indicator showing the cursor.
So, on input and click we need to append a marker element to the document body if there isn’t one. We also need to calculate the correct position for that marker on every input or click. As a bonus, if we click off of the input, we want the marker to disappear.
This is just a basic demo, it’s not polished 💍 by any means 😅
The important things to take notice of here is what we do with the returned cursor position for our input
.
In this scenario, we get the foundation cursor position first. But then we account for any custom styling and any scrolling that may have happened. Using Math.min
we can ensure that there is a limit on the markers position in certain directions. Consider the left
position. We don’t want the marker to go any further left than the furthest right of the input minus any padding so we limit that. In the demo, I’ve applied a basic position to the marker and then offset the indicator using basic CSS
transforms 🤓
As an added bonus, when we create the marker, we add an event listener to the document.body
to listen for clicks. This is so that when we click off of the input
the marker can be removed from the DOM
.
That is one basic scenario we can use our obtained cursor position in. See the demo for other possible use cases such as type aheads and selection highlighting.
Wrapping up
Not the most common article topic. But, an interesting problem with an interesting solution that I felt was worthy of sharing.
Be sure to check out the demo and code 🤓
Hopefully this can help you out if you come across the same problem. There may indeed be better ways to solve this and if you find one I’d certainly like to see it 👍 😃
As always, any questions or suggestions, please feel free to leave a response or tweet me 🐦!