Создание, настройка стилей на диапазоне

Я пытаюсь автоматически определить адреса на странице и добавить адрес класса, где он найден.

var rangyPatternApplier = function(element, pattern, style) {
  var innerText = element.innerText;
  var matches = innerText.match(pattern);
  if (matches) {
    for (var i = 0; i < matches.length; i++) {
      console.log("Match: " + matches[i]);
      var start = innerText.indexOf(matches[i]);
      var end = start + matches[i].length;

      let range = document.createRange();
      var start = innerText.indexOf(matches[i]);
      console.log('inner text: ' + innerText);
      console.log('start: ' + start);
      console.log('starts with: ' + innerText.substring(start));
      var end = start + matches[i].length;
      var startNode = element.childNodes[0];
      var endNode = startNode;
      while (startNode.nodeValue.length < start) {
        start -= startNode.nodeValue.length;
        end -= startNode.nodeValue.length;
        startNode = startNode.nextSibling;
        endNode = startNode;
        if (startNode == null) {
          error.reportError("Just wrong in Sections.rangyPatternApplier");
          return;
        }
      }
      while (endNode.nodeValue.length < end) {
        end -= endNode.nodeValue.length;
        if (endNode.nextSibling) endNode = endNode.nextSibling;
        while (!endNode.nodeValue) {
          endNode = endNode.childNodes[0];
        }
        if (endNode == null) {
          error.reportError("Just wrong in Sections.rangyPatternApplier");
        }
      } 
      range.setStart(startNode, start);
      console.log("starts with: " + startNode.nodeValue.substring(start));
      range.setEnd(endNode, end);

          var applier  = rangy.createClassApplier(style, {
                    elementTagName: "span",
                    elementProperties: {
                    },
          });
      window.getSelection().addRange(range);
      applier.toggleSelection();
    }
  }
}

Звонил через:

  $("P").each(function () {
    rangyPatternApplier(this, new RegExp("\\d+\\s[A-z]+\\s[A-z0-9]+\\s(Street|St|Avenue|Av|Ave|Road|Rd)", "mgi"), "Address");
  });

По тексту в абзаце:

If the income renders the household ineligible for CA/CILOCA, the case will be systemically referred to the Administration for Children s Services Transitional Child Care Unit at 109 East 16th Street 3rd floor for evaluation of Transitional Child Care (TCC) benefits. The TCC Worker determines eligibility for up to 12 months of TCC benefits.

Регулярное выражение работает, класс адреса применяется. Я применяю диапазон к выбору окна, потому что в rangy возникает ошибка при применении только к диапазону (я получаю сообщение об ошибке). Но каким-то образом, когда я создаю диапазон, диапазон появляется за 5 символов до начала адреса и заканчивается на 9 символов раньше. Ранняя концовка может быть связана с тегом вокруг th на 16-й улице. Но почему диапазон на 5 символов раньше, чем тот, что я нахожу в innerText?


person ControlAltDel    schedule 15.09.2020    source источник


Ответы (1)


Боже, это было больно, но у меня это сработало. Добавляю сюда свое решение, так что, надеюсь, хотя бы нескольким людям не придется делать что-то, что, на мой взгляд, должно быть гораздо более встроенным.

//nextTextNode is for getting the next text node from the DOM

function nextTextNode(node) {
  if (node.nodeType == 1) { //element
    while (node.nodeType != 3) {
      node = node.firstChild;
    }
    return node;
  }
  if (node.nodeType == 3) { //text node
    if (node.nextSibling) {
      if (node.nextSibling.nodeType == 3) {
        return node.nextSibling;
      } else {
        return nextTextNode(node.nextSibling);
      }
    } else {
      while (!node.nextSibling) {
        node = node.parentNode;
        if (!node) return null;
      }
      if (node.nextSibling.nodeType == 3) {
        return node.nextSibling;
      } else {
        return nextTextNode(node.nextSibling);
      }
    }
  } else {
    throw new Error("nextTextNode: Node is either null, not connected to the DOM, or is not of node type 1 or 3");
  }
}

А затем создать диапазон. Текстовые узлы имеют дополнительные символы новой строки и пробела по сравнению с element.innerText. В приведенной ниже функции я отслеживаю как количество дополнительных символов, так и общее количество символов, чтобы отслеживать несоответствия между innerText и node.nodeValue и количество символов в нем.

function createRangeForString(startElement, text) {
  var extras = 0;
  var innerText = startElement.innerText;
  var start = innerText.indexOf(text);
  if (start === -1) throw new Error ("createRangeForString. text: " + text + " not found in startElement");
  var textNode = nextTextNode(startElement);
  var totalCharsSeen = 0;
  var range = document.createRange();
  for (var i = 0; i < start; i++) { // I don't think I have to add extras in limit for i. Is already included
    if ((i + extras) - totalCharsSeen >= textNode.nodeValue.length) { //check if textNode is long enough
      totalCharsSeen += textNode.nodeValue.length;
      textNode = nextTextNode(textNode);
    }
    while (textNode.nodeValue.charAt(i + extras - totalCharsSeen) == "\n") {
      extras++;
    }
    while (textNode.nodeValue.charAt(i + extras - totalCharsSeen) == " " && innerText.charAt(i) != " ") {
      extras++;
    }
  }
  range.setStart(textNode, i + extras - totalCharsSeen);

  var end = start + text.length;

  for (var i = start + 1; i < end; i++) { // I don't think I have to add extras in limit for i. Is already included
    if ((i + extras) - totalCharsSeen >= textNode.nodeValue.length) { //check if textNode is long enough
      totalCharsSeen += textNode.nodeValue.length;
      textNode = nextTextNode(textNode);
    }
    while (textNode.nodeValue.charAt(i + extras - totalCharsSeen) == "\n") {
      extras++;
    }
    while (textNode.nodeValue.charAt(i + extras - totalCharsSeen) == " " && innerText.charAt(i) != " ") {
      extras++;
    }
  }
  range.setEnd(textNode, i + extras - totalCharsSeen);
  return range;
}
person ControlAltDel    schedule 16.09.2020