问题
Markdown-like functionality for tooltips
Problem:
Using Vanilla JavaScript I want to:
Change this:
<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>
To this:
<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>
so it looks visually like this:
is somehow similar to "markdown" edit functionalities.
Solution:
- Mark the strings to replace in a different way:
<p>It is fine *[thanks]{a word that expresses gratitude}*!</p>
- Initiate Bootstrap and tooltip functionality.
- Grab all paragraphs
var p = document.getElementsByTagName('p')
- Apply REGEX
tooltip = original.match(/(\{)(.*?)(\})/gi)[0].slice(1, -1);
hint = original.match(/(\[)(.*?)(\])/gi)[0].slice(1, -1);
- Change their inside-text
replaced = original.replace(/(\*)(.*?)(\*)/gi,
`<mark data-toggle="tooltip" data-placement="top" title="${tooltip}">${hint}</mark>`);
elem.innerHTML = replaced;
- Alltogether in one function:
[].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;
});
but I fail
Miserable when there is more paragraphs or when I just want to do it in an easy way with 2 pair of brackets instead of additional asterix. Fails also hen the innerTEXT has more phrases / words that should have the tooltip. Any ideas? Do you have any suggestions? Existing ways of doing it? Libraries? Scripts?
回答1:
One very easily can stumble at coming up with the right approach of how to replace a text node with other unknown HTML content.
A generic solution takes into account a more complex HTML content.
Thus, starting from a source-node, one stepwise needs to insert each of its child-nodes (either text- or element-nodes) before the targeted text-node. Once all nodes got inserted, one finally removes the targeted text-node.
Regarding the regex and the markup template, one can create the markup-string within a single replace
call from a single regex and a single template string both making use of Capturing Groups.
// 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>
//-->
来源:https://stackoverflow.com/questions/62818016/how-to-query-text-nodes-from-dom-find-markdown-patterns-replace-matches-with-h