Найти узел с учетом позиции курсора (javascript)

У меня есть файл HTML, и у меня есть предполагаемая позиция каретки. С JS мне нужно найти html-узел, на котором была бы размещена каретка, если бы она находилась в заданной позиции.

function findNodeForCaretPosition(caretPosition) {
    var node = null; // type Node (https://developer.mozilla.org/en-US/docs/Web/API/Node)
  // TODO find node
  return node;
}


Пример HTML

<html>
<body>
    <div>
        <p id = "a">Time waits for no man. Unless that man is Chuck Norris.</p>
    </div>
    <div>
        <p id = "b">Chuck Norris can touch <span id = "c" style = "color:blue">MC Hammer</span>.</p>
    </div>
</body>
</html>


Пример обычного текста HTML

Time waits for no man. Unless that man is Chuck Norris.

Chuck Norris can touch MC Hammer.


Тесты

Позиция каретки = 4 (время | ожидания)
Ответ = <p id = "a">

Положение каретки = 16 (можно | коснуться).
Ответ = <p id = "b">

Положение каретки = 25 (MC | Молоток).
Ответ = <span id = "c">

Позиция каретки = 1000), нет ответа (null).

Вы можете использовать NodeIterator для перебора всех текстовых узлов и подсчета их символов. Вы также можете использовать API Selection.

Titus 11.07.2019 15:19

Какой вариант использования здесь? Зачем нужно такое отображение?

Tarun Lalwani 14.07.2019 19:09

@TarunLalwani Мне нужно перенести локаторы чтения из одной библиотеки чтения epub в другую. Старый хранит место чтения как позицию каретки в обычном тексте. Новый хранит его в формате CFI (Canonical Fragment Identifier). Мне нужно сделать преобразование между ними. Для CFI мне нужно знать узел, который хотя бы приблизительно соответствует положению каретки. Есть право на ошибку. Мне позволено промахнуться на несколько узлов. Но в целом ответ Узел должен быть близок к положению каретки.

Egis 14.07.2019 19:32

Что такое положение каретки относительно? Начало строки?

jhpratt 15.07.2019 04:05

@jhpratt Да. В моем примере, если позиция каретки = 0, она находится прямо в начале строки, как здесь |Time waits.

Egis 15.07.2019 08:00

Безопасно ли работать, предполагая, что это только текст, а не изображения/диаграммы/что-то еще?

jhpratt 15.07.2019 08:06

@jhpratt на практике будут случаи с изображениями / диаграммами и т. д. Но наиболее распространенным случаем является то, что html будет содержать только текстовые элементы, поэтому да, вы можете работать с этим предположением. Если функция не может вернуть правильный узел, когда есть изображения/диаграммы - меня это устраивает.

Egis 15.07.2019 08:11

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

jhpratt 16.07.2019 02:24
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
8
286
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я довольно много прокомментировал код, поэтому не думаю, что он нуждается в дополнительных пояснениях. Просто вызовите caretPosition с родительским узлом и индексом в качестве параметров. Поскольку строки в JavaScript имеют кодировку UTF-16, любые смайлики или символы, отличные от ASCII, должен считаются одним символом, а не несколькими.

Следует отметить, что пробелы важны. Таким образом, HTML из вашего вопроса технически не будет работать, так как он сначала считает новые строки и начальные пробелы. Я удалил это здесь для простоты.

Если у вас есть какие-либо вопросы, дайте мне знать в комментариях.

/**
 * @param {Element} parent
 * @param {number} index
 */
function caretPosition(parent, index) {
  // The index is too large to fit in the element, return `null` per requirements.
  // We also return `null` if the element isn't a text or element node,
  // as there is no text to check against.
  if (
    (parent.nodeType === Node.ELEMENT_NODE && index > parent.innerText.length)
    || (parent.nodeType === Node.TEXT_NODE && index > parent.data.length)
    || ![Node.ELEMENT_NODE, Node.TEXT_NODE].includes(parent.nodeType)
  ){
    return null;
  }

  // The length of all text combined to this point
  // (zero is the beginning of `parent`).
  let combinedLength = 0;

  // Iterate over the children until we find
  // the element where we cross the boundry.
  for (const child of parent.childNodes) {
    // Store this in case we need to recurse.
    const previousLength = combinedLength;

    // For the current child, add the length of its text content.
    // As text and element nodes don't share a common property,
    // we need to explicitly check for both. Other node types
    // (such as comments) are irrelevant to the task at hand.
    if (child.nodeType === Node.TEXT_NODE) {
      combinedLength += child.data.length;
    } else if (child.nodeType === Node.ELEMENT_NODE) {
      combinedLength += child.innerText.length;
    } else {
      // We don't have a text or element node,
      // so there's no text that we could care about.
      // The recursive case will handle the fact that nothing changed,
      // and will return `null`.
      continue;
    }

    // Our cursor is inside or at the end of this node.
    if (index <= combinedLength) {
      // We are in a text node and have nothing to recurse on.
      // Return the parent element of the text node,
      // which is a DOM element.
      if (child.nodeType === Node.TEXT_NODE) {
        return child.parentElement;
      }

      // If we are in an element node, then we have no children.
      // Without children, there is nothing to recurse on;
      // we should return the element.
      // If we are _not_ in an element node,
      // this will be `false`, and we will enter the recursive case.
      else if (child.childElementCount === 0) {
        return child;
      }

      // We have children to iterate over, so do that.
      // It is necessary to change the index to search for
      // as we've got a new reference frame.
      return caretPosition(child, index - previousLength);
    }
  }
}

// Make sure everything works!

const div = document.querySelectorAll('div');

console.assert(caretPosition(div[0], 4) === document.querySelector('#a'));
console.assert(caretPosition(div[1], 16) === document.querySelector('#b'));
console.assert(caretPosition(div[1], 25) === document.querySelector('#c'));
console.assert(caretPosition(div[0], 1000) === null);
console.assert(caretPosition(div[1], 1000) === null);
<div><p id='a'>Time waits for no man. Unless that man is Chuck Norris.</p></div>
<div><p id='b'>Chuck Norris can touch <span id='c' style='color:blue'>MC Hammer</span>.</p></div>

Другие вопросы по теме