Nano Hash - криптовалюты, майнинг, программирование

Получить полное слово, которое курсор касается (или внутри) JavaScript

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

// 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. Диапазон или 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 от друга.

04.05.2020

  • Не могли бы вы привести более подробный пример с кодом html + js? Кроме того, я посмотрел ваши ссылки, кажется, Range делает то, что вы ищете. Я бы настаивал на этом лично. Наконец, вы искали полифилл/библиотеку, которая облегчила бы вашу задачу и сделала бы ее кроссбраузерной? 05.05.2020
  • Спасибо @FlorentRoques за положительный и полезный комментарий, слишком редкий для SO. Я опубликовал ответ, который работает. Однако я не уверен, является ли это окончательным решением для поиска слов или существует ли tool for the job с диапазонами. (Я прочитал все эти ссылки дважды) 05.05.2020
  • Диапазоны не применяются к полям ввода, так что нет, это не тот инструмент, который вам нужен. 09.05.2020
  • Эй, @Kaiido, проверь ссылки. Я / мы в настоящее время отлично используем их для ввода. 09.05.2020
  • ... Диапазоны не применяются для полей ‹input›. Либо вы не используете ‹input›, либо вы не используете Диапазон Для ввода текста вы можете использовать их значения selectionStart, selectionEnd, но диапазоны не будут иметь доступа к значениям ‹ввода›. jsfiddle.net/x15a7dek 09.05.2020
  • Да Диапазоны используются только для элементов и текстовых узлов, это правильно, но входные данные имеют другой API, который включает некоторые из тех же инструментов, таких как setSelectionRange и setRangeText и т. д. html.spec.whatwg.org/#textFieldSelection 09.05.2020
  • Итак... Диапазоны - это не тот инструмент, который вам нужен. 10.05.2020
  • Что ж, @Kaiido использует API для ввода, основанный на спецификации Ranges. У вас есть что добавить к этому вопросу? 10.05.2020
  • Это не так. Там нет ни одного из доступных методов Range API. Ваш вопрос спрашивает, как использовать диапазоны для вашего случая. Я указываю, что вы этого не сделаете, потому что диапазоны не обрабатывают поля ввода. 10.05.2020
  • Вы хотите получить информацию о каретке в contenteditables? 10.05.2020
  • Привет, @OrkhanAlikhanov, нет, это не обязательно, спасибо. 10.05.2020
  • Спасибо, @Kaiido, я понимаю, о чем ты говоришь. 10.05.2020

Ответы:


1

Если вы определяете «слово» как любую последовательность символов, разделенных пробелом, то вы можете просто использовать 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">

10.05.2020
  • Отличный ответ @Kaiido, вы говорите, что это можно сделать без RegEx? Является ли это более эффективным, чем приведенное ниже решение Гродзи? 10.05.2020
  • @BenRacicot Их решение тоже хорошее, по крайней мере, вероятно, лучше, чем мой второй фрагмент. Теперь, что более эффективно между методами RegEx и String, полностью зависит от ввода (тестируемого текста): его размер, тип символов и т. д. будут иметь существенные изменения для обоих методов. Если вы знаете, какие входные данные вы получите, выполните тесты на образцах и выберите наименее худший ;-) 11.05.2020
  • Я думаю, ты скромничаешь, @Kaiido. с точки зрения производительности ваш поиск строк выполняется быстрее, чем мои регулярные выражения. Это действительно ... требует доказательств, но мне это кажется неуместным: я думаю, что проблема заключается скорее в том, «какой набор символов определяет разделители», чем в производительности. В общем, хорошо, что есть варианты 11.05.2020
  • Я хотел поблагодарить вас за этот ответ, Кайидо. Это может быть даже лучший ответ, и я действительно ценю проделанную вами работу. Однако в итоге я использовал ответ Гродзи, и поэтому я наградил его наградой. 15.05.2020
  • @BenRacicot, ты очень хорошо справился, их ответ заслуживает награды не меньше, чем этот, и им, вероятно, нужно больше этих очков единорога, чем мне (у меня уже есть все инструменты модерации, которые я могу выиграть) 15.05.2020

  • 2

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

    • любая строка, содержащая символ слова, оканчивающаяся на 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>

    10.05.2020
  • Действительно впечатляет @grodzi, это может быть самым эффективным. Я применил свою настройку CodePen, и она отлично работает. 10.05.2020
  • p и n это RegExpMatchArray. Итак, похоже, нам нужно p[0] + n[0]. 11.05.2020
  • edit: tldr, вы можете написать p[0]+n[0], но это не обязательно. Это немного тонко: в случае совпадения массив имеет форму ['matched', index: 5] (см.). При использовании дополнения массив преобразуется в строку, и ... toString в этом массиве зависит от ArrayIteratorPrototype [ @@toStringTag], который перебирает свойства [length] этого массива. Поскольку я никого не захватил (без групп в скобках), массив имеет длину 0 + 1, и мы получаем только первый результат, который «совпадает» 11.05.2020
  • Понял, кажется, у TS плохое применение спецификации RegExpMatchArrays. Это работает для ТС return <string>(p || '') + (n || ''); 11.05.2020

  • 3

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

    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!">

    10.05.2020
  • Привет @TaufikNurrohman Это очень креативно, но я не могу заставить его работать с CodePen. Можете ли вы применить его к тестовой установке там? codepen.io/BRacicot/pen/WNQJzgL 10.05.2020
  • Выбранное слово сохраняется в переменной word, которую я прокомментировал в строке 12. Поместите значение этой переменной где-нибудь в HTML. И исходя из вашего примера, это должно быть сделано с document.getElementById('word').textContent = word; 11.05.2020

  • 4

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

    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];
    }
    

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

    05.05.2020
  • codereview может оказаться полезным для вас, если вы хотите улучшить то, что уже работает :) 09.05.2020
  • Много накладных расходов, разбивающих подстроку от 0 до позиции каждый раз. Лучше выполнить цикл от позиции назад на 2, чтобы найти границу /\W(\w)/, а затем подстроку этой позиции до конца, чтобы найти передний конец слова /\w\b/ 09.05.2020
  • Это именно то, о чем я думал! Но раз уж я здесь, то решил спросить 1. как именно это сделать. 2. если есть более действенные способы. 10.05.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..