javascript - 如何从 DOM 中查询文本节点、查找降价模式、用 HTML 标记替换匹配项以及用新内容替换原始文本节点?
问题描述
工具提示的类似 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>
所以它在视觉上看起来像这样:
在某种程度上类似于“降价”编辑功能。
解决方案:
- 以不同的方式标记要替换的字符串:
<p>It is fine *[thanks]{a word that expresses gratitude}*!</p>
- 启动引导和工具提示功能。
- 抓住所有段落
var p = document.getElementsByTagName('p')
- 应用正则表达式
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;
});
但我失败了
当有更多段落或者我只想用 2 对括号而不是额外的 asterix 以简单的方式做到这一点时,会很痛苦。当 innerTEXT 有更多应该有工具提示的短语/单词时也会失败。有任何想法吗?你有什么建议吗?现有的做法?图书馆?脚本?
解决方案
一个人很容易偶然发现如何用其他未知的 HTML 内容替换文本节点的正确方法。
通用解决方案考虑了更复杂的 HTML 内容。
因此,从源节点开始,逐步需要在目标文本节点之前插入其每个子节点(文本节点或元素节点)。一旦所有节点都被插入,最终删除目标文本节点。
关于regex 和 markup template ,可以在单个 regex 和单个模板字符串replace
的单个调用中创建标记字符串,两者都使用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>
//-->
推荐阅读
- c++ - C ++将“字符串”与数组中的对象进行比较
- swift - 响应请求的分页网址的最佳做法是什么
- c# - Entity Framework 添加很多实体,嵌套属性已经存在
- html - 移动导航栏上的模糊背景
- hadoop - 尝试使用hadoop创建单节点集群
- objective-c - 在 UIContainerView 中为 2 个 UIViewController 设置动画
- rest - SharePoint rest api 以获取组成员的完整详细信息
- node.js - 节点 https 客户端 'GET_SERVER_HELLO:sslv3 警报握手失败',但在浏览器中有效
- rxjs - 错误:rxjs/operator/debounceTime 没有导出的成员 'debounceTime'
- reactjs - 使用 React-Router 在 Reactjs 中重定向