Josh O'Connor
Josh O'Connor

Reputation: 4962

Detect if word at selectionStart is a URL

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?

Upvotes: 1

Views: 172

Answers (2)

ggorlen
ggorlen

Reputation: 57259

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>

Upvotes: 1

HARI CHARAN K
HARI CHARAN K

Reputation: 79

The below regex should work for any URL and can match all domains too.

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(/(http|https):\/\/(www.)?([a-zA-Z0-9]+)/g)[0] //logs http://www.go
var b = fullText.substring(split).match(/^[a-zA-Z0-9]*.[a-z]{2,9}/)[0] //logs ogle.com

Upvotes: 0

Related Questions