Как запросить текстовые узлы из DOM, найти шаблоны разметки, заменить совпадения на HTML -марку и заменить исходный текстовый узел новым содержимым? - PullRequest
1 голос
/ 09 июля 2020

Подобная Markdown функциональность для всплывающих подсказок

Проблема:

Использование Vanilla JavaScript Я хочу:

Изменить это:

<div>
   <p>
        Hello [world]{big round planet we live on}, how is it [going]{verb that means walking}? 
   </p>
   <p>
        It is [fine]{a word that expresses gratitude}.
   </p>
</div> 

На это:

<div>
    <p>
        Hello <mark data-toggle="tooltip" data-placement="top" title="big round planet we live on">world</mark>, how is it <mark data-toggle="tooltip" data-placement="top" title="verb means walking">world</mark>?
    </p>
    <p>
        It is fine <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">thanks</mark>.
    </p>
</div> 

визуально это выглядит так:

enter image description here

is somehow similar to "markdown" edit functionalities.

Solution:

  1. Mark the strings to replace in a different way:
It is fine *[thanks]{a word that expresses gratitude}*!
  1. Initiate Bootstrap and функция всплывающей подсказки .
  2. Захватить все абзацы
var p = document.getElementsByTagName('p')
Применить REGEX
tooltip = original.match(/(\{)(.*?)(\})/gi)[0].slice(1, -1);
hint = original.match(/(\[)(.*?)(\])/gi)[0].slice(1, -1);
Изменить их внутренний текст
replaced = original.replace(/(\*)(.*?)(\*)/gi, 
        `<mark data-toggle="tooltip" data-placement="top" title="${tooltip}">${hint}</mark>`);
elem.innerHTML = replaced;
Все вместе в одной функции:
[].forEach.call(p, elem => {
    let original = elem.innerHTML;
    let replaced, tooltip, hint
    tooltip = original.match(/(\{)(.*?)(\})/gi)[0].slice(1, -1);
    hint = original.match(/(\[)(.*?)(\])/gi)[0].slice(1, -1);
    replaced = original.replace(/(\*)(.*?)(\*)/gi, 
        `<mark data-toggle="tooltip" data-placement="top" title="${tooltip}">${hint}</mark>`);
      elem.innerHTML = replaced;
});

но я не могу

Жалко, когда абзацев больше, или когда я просто хочу сделать это простым способом с двумя парами скобок вместо дополнительной звездочки. Не работает также, когда в innerTEXT есть больше фраз / слов, которые должны иметь всплывающую подсказку. Любые идеи? Есть ли у вас какие-либо предложения? Существующие способы сделать это? Библиотеки? Скрипты?

1 Ответ

1 голос
/ 09 июля 2020

Очень легко найти правильный подход к замене текстового узла другим неизвестным HTML содержимым.

Общее c решение учитывает более сложный HTML content.

Таким образом, начиная с исходного узла, нужно поэтапно вставить каждый из его дочерних узлов (текстовых или элементных узлов) перед целевым текстовым узлом. Как только все узлы вставлены, один, наконец, удаляет целевой текстовый узел.

Что касается регулярного выражения и шаблона разметки , можно создать строку разметки в рамках одного вызова replace из одно регулярное выражение и одна строка шаблона, оба используют Группы захвата .

// text node detection helper
function isNonEmptyTextNode(node) {
  return (
       (node.nodeType === 3)
    && (node.nodeValue.trim() !== '')
    && (node.parentNode.tagName.toLowerCase() !== 'script')
  );
}

// text node reducer functionality
function collectNonEmptyTextNode(list, node) {
  if (isNonEmptyTextNode(node)) {
    list.push(node);
  }
  return list;
}
function collectTextNodeList(list, elmNode) {
  return Array.from(
    elmNode.childNodes
  ).reduce(
    collectNonEmptyTextNode,
    list
  );
}

// final dom render function
function replaceTargetNodeWithSourceNodeContent(targetNode, sourceNode) {
  const parentNode = targetNode.parentNode;

  Array.from(sourceNode.childNodes).forEach(function (node) {
    parentNode.insertBefore(node, targetNode);
  });
  parentNode.removeChild(targetNode);    
}

// template and dom fragment render function
function findMarkdownCreateMarkupAndReplaceTextNode(node) {
  const regX = (/\[([^\]]+)\]\{([^\}]+)\}/g);
  const text = node.nodeValue;

  if (regX.test(text)) {
    const template = '<mark data-toggle="tooltip" data-placement="top" title="$2">$1</mark>'

    const renderNode = document.createElement('div');
    renderNode.innerHTML = text.replace(regX, template);

    replaceTargetNodeWithSourceNodeContent(node, renderNode);
  }
}


const elementNodeList = Array.from(document.body.getElementsByTagName('*'));

const textNodeList = elementNodeList.reduce(collectTextNodeList, []);

textNodeList.forEach(findMarkdownCreateMarkupAndReplaceTextNode);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<div>
  <p>
    <span>Hello [world]{big round planet we live on}, how is it [going]{verb that means walking}?</span>
    <span>Hello [world]{big round planet we live on}, how is it [going]{verb that means walking}?</span>
  </p>
  <p>
    <span>It is [fine]{a word that expresses gratitude}.</span>
    It is [fine]{a word that expresses gratitude}.
    <span>It is [fine]{a word that expresses gratitude}.</span>
  </p>
</div>
<!--

  // does get rerendered into:

  <div>
    <p>
      <span>
        Hello
        <mark data-toggle="tooltip" data-placement="top" title="big round planet we live on">
          world
        </mark>
        , how is it
        <mark data-toggle="tooltip" data-placement="top" title="verb that means walking">
          going
        </mark>
        ?
      </span>
      <span>
        Hello
        <mark data-toggle="tooltip" data-placement="top" title="big round planet we live on">
          world
        </mark>
        , how is it
        <mark data-toggle="tooltip" data-placement="top" title="verb that means walking">
          going
        </mark>
        ?
      </span>
    </p>
    <p>
      <span>
        It is
        <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">
          fine
        </mark>
        .
      </span>
      It is
      <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">
        fine
      </mark>
      .
      <span>
        It is
        <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">
          fine
        </mark>
        .
      </span>
    </p>
  </div>

//-->
...