Получить полное слово, к которому прикасается курсор (или в пределах) JavaScript - PullRequest
4 голосов
/ 04 мая 2020

С собственным полем ввода я могу получить позицию курсора просто отлично. Также любой выделенный текст достаточно прост.

// get cursor position
console.log(input.selectionStart);
// get highlighted text
console.log(input.value.substr(input.selectionStart, input.selectionEnd - input.selectionStart));

Но как мы можем получить полное слово, к которому прикасается курсор?

this is a |sent|ence|

Вопрос:

Если каждая труба является потенциальной позицией курсора, если стрелка нажата или нажата внутри и вокруг слова, как мы можем получить полное слово «предложение»?

Подвопросы:

  1. Range или RegEx для этого решения?
  2. Может ли RegEx поиск по позиции в строке?
  3. Может ли Regex найти слово, если задана позиция символа в строке

Исследование:
https://javascript.info/selection-range.
https://developer.mozilla.org/en-US/docs/Web/API/range.
https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint (только Chrome)

Работа CodePen с RegEx от друга.

Ответы [ 4 ]

3 голосов
/ 10 мая 2020

Если вы определяете «слово» как любую последовательность символов, разделенных пробелом, то вы можете просто использовать String.prototype.lastIndexOf и String.prototype.indexOf для поиска пробела до и после позиции курсора, а затем для получения подстроки из этого диапазона:

const inp = document.querySelector('input');
document.onselectionchange =
  inp.onselect = inp.onclick = // Firefox doesn't fire selectionchange in <input>
    (evt) => {
  const text = inp.value;
  const start_index = inp.selectionStart;
  const end_index = inp.selectionEnd;
  const previous_space_index = text.lastIndexOf( " ", start_index - 1 );
  const next_space_index = text.indexOf( " ", end_index );
  const begin = previous_space_index < 0 ? 0 : previous_space_index + 1;
  const end = next_space_index < 0 ? text.length : next_space_index;
  const between_spaces = text.substring( begin, end );

  console.log( between_spaces );
};
<input type="text" value="this is a sentence">

Если вам действительно нужно определить «слово» как любую последовательность символов, соответствующих /\w/, то это немного более запутанно:

const inp = document.querySelector('input');
document.onselectionchange =
  inp.onselect = inp.onclick = // Firefox doesn't fire selectionchange in <input>
    (evt) => {
  const text = inp.value;
  const start_index = inp.selectionStart;
  const end_index = inp.selectionEnd;
  // search in the before substring 
  const before_text = text.substring( 0, start_index );
  // for all non word characters
  const before_match = before_text.match( /\W/g );
  // get the last one
  const last_before_match = before_match && before_match[ before_match.length - 1 ];
  // retrieve its index
  const previous_nonword_index = last_before_match ? text.lastIndexOf( last_before_match, start_index - 1 ) : -1;
  const begin = previous_nonword_index < 0 ? 0 : previous_nonword_index + 1;
  
  // search in the after substring 
  const after_text = text.substring( end_index );
  // for the first occurence of a non word character
  const next_nonword_index = after_text.search( /\W/ );
  // remember to add the length of the beginning string to the found index
  const end = next_nonword_index < 0 ? text.length : next_nonword_index + end_index;
  
  const between_spaces = text.substring( begin, end );
  console.log( between_spaces );
};
<input type="text" value="this undefined is a sæntence wîth nøn word characters" size="40">
2 голосов
/ 10 мая 2020

Вы можете использовать некоторое регулярное выражение для поиска

  • любой строки, содержащей символ слова, заканчивающийся на position
  • любой строки, содержащей символ слова, начинающийся с position

const [$show, $input] = document.querySelectorAll('span,textarea')
const getWord = (s, pos) => {
  const n = s.substring(pos).match(/^[a-zA-Z0-9-_]+/)
  const p = s.substring(0, pos).match(/[a-zA-Z0-9-_]+$/)
  // if you really only want the word if you click at start or between
  // but not at end instead use if (!n) return
  if(!p && !n) return ''
  return (p || '') + (n || '')
}
const showWord = e => $show.innerText = getWord(e.target.value, e.target.selectionStart)
$input.addEventListener('click', showWord)
$input.addEventListener('input', showWord)
textarea{
  width: 500px;
  height: 500px;
}
<span id="inputshow"></span><br/>
<textarea>In computer science, an x-fast trie is a data structure for storing integers from a bounded domain. It supports exact and predecessor or successor queries in time O(log log M), using O(n log M) space, where n is the number of stored values and M is the maximum value in the domain. The structure was proposed by Dan Willard in 1982,[1] along with the more complicated y-fast trie, as a way to improve the space usage of van Emde Boas trees, while retaining the O(log log M) query time. </textarea>
1 голос
/ 10 мая 2020

Сначала примите все несловарные символы в виде канала, затем увеличьте смещение каретки до ближайшей позиции символа канала.

let input = document.querySelector('input');

input.addEventListener('click', function() {
    // We only need the character offset here
    // Assume all non-word character(s) is a pipe
    let test = this.value.replace(/\W/g, '|');
    let caret = this.selectionStart;
    // Get the last word part length before caret
    let indexBeforeCaret = test.substring(0, caret).split('|').pop().length;
    // Get the first word part length after caret
    let indexAfterCaret = test.substring(caret).split('|')[0].length;
    // let word = this.value.substring(caret - indexBeforeCaret, caret + indexAfterCaret);
    // console.log(word);
    // Expand current caret position to the whole word
    this.selectionStart = caret - indexBeforeCaret;
    this.selectionEnd = caret + indexAfterCaret;
}, false);
<input type="text" value="this is another sentence!">
0 голосов
/ 05 мая 2020

Я выложу свой собственный ответ, хотя он не отвечает на вопрос о диапазонах. Это все еще в воздухе.

CodePen

// Thanks to Yash Mathur for this solution
public getWordAtNthPosition(str: string, position: number) {
    if (!/^[A-Z]|^[0-9]|\s$/i.test(str[position])) {
        return null;
    }
    const nWord = str.substr(0, position).split(/\W+/g).length;
    const r = new RegExp(`(?=((\\w+)\\W*){${nWord}})`, 'g');
    const segs = r.exec(str);
    if (!segs || !segs.length) {
        return null;
    }
    return r.exec(str)[2];
}

Возможно, кто-то может оптимизировать это дальше?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...