I am trying to detect the full word the user is tapping when they focus in on an HTML input element, and then see if it is a URL. I am using the following method:
let el = this.reminderInput.nativeElement
var fullText = el.value;
console.log(fullText) //logs 'test http://www.google.com'
var split = el.selectionStart
console.log(split) //logs 18
var a = fullText.substring(0, split).match(/[A-Za-z]*$/)[0]
var b = fullText.substring(split).match(/^[A-Za-z]*/)[0]
I have the following string in my input element:
test http://www.google.com
In this example, if the user clicked in between the o
's in google, a
will print out go
and b
will print out gle
.
I'd like for a
to print out http://www.go
and b
to print out ogle.com
, and thus, when I combing a + b
, I'd get the full string http://www.google.com
and check if it is a valid URL, and if so, open it in another tab.
How do I update my regex to only detect if the word is not a whitespace/return so I can get the full word of the selection and check if it is a URL?
Here's a general idea: match all non-whitespace chunks but preserve indexes of each match. Then iterate over the matches looking for the word that best fits target.selectionStart
. From there, it's a known problem to detect if that word is a URL.
const findWordUnderCursor = e => {
const words = [];
for (let m, re = /\S+/g; m = re.exec(e.target.value);) {
words.push([m[0], m.index]);
}
const cursor = e.target.selectionStart;
const target = words.find(([word, i]) => i + word.length >= cursor);
document.querySelector("#output").innerText = `{${target ? target[0] : ""}}`;
};
const inpElem = document.querySelector("input");
inpElem.addEventListener("keyup", findWordUnderCursor);
inpElem.addEventListener("click", findWordUnderCursor);
inpElem.addEventListener("blur", e => {
document.querySelector("#output").innerText = "";
});
* { font-family: monospace; }
<input value="foo test http://www.google.com bar" size=40>
<div id="output"></div>
If you're using a framework (it looks like you are), replace the DOM interaction with your framework's calls.
If the idea of matching and iterating the entire input seems non-performant, you can always go the pointer route and walk forwards and backward from selection start until you hit whitespace on either end:
const findWordUnderCursor = e => {
let start = e.target.selectionStart;
let end = start;
const val = e.target.value;
for (; start > 0 && /\S/.test(val[start-1]); start--);
for (; end < val.length && /\S/.test(val[end]); end++);
document.querySelector("#output").innerText = `{${val.slice(start, end)}}`;
};
const inpElem = document.querySelector("input");
inpElem.addEventListener("keyup", findWordUnderCursor);
inpElem.addEventListener("click", findWordUnderCursor);
inpElem.addEventListener("blur", e => {
document.querySelector("#output").innerText = "";
});
* { font-family: monospace; }
<input value="foo test http://www.google.com bar" size=40>
<div id="output"></div>