什么是编写 real-time 文本搜索和突出显示功能的足够好的方法,它不会破坏文本和 element-nodes 的顺序
What is a good enough approach for writing real-time text search and highlight functionality which does not break the order of text- and element-nodes
我正在使用以下代码来突出显示 div 中的文本。但是,如果我输入一些简单的东西,如“a”、“img”等,它会破坏 html 输出、图像并破坏网站。
if ($('#block-multiblock-2 input').val().length !== 0) {
$('.group-informacie .field-name-body p').each(function() {
//Handle special characters used in regex
var searchregexp = new RegExp($("#block-multiblock-2 input").val().replace(/[.*+?^${}()|[\]\]/g, '\$&'), "gi");
//$& will maintain uppercase and lowercase characters.
$(this).html($(this).html().replace(searchregexp, "<span class='highlight'>$&</span>"));
});
}
我认为问题在于必须以某种方式排除 html 标记的 RegExp?我尝试插入在其他问题中发现的 <> 左右的字符,但实际上没有任何效果。
我试图在用户/ckeditor 保存的文本中进行 jquery 搜索,输出有时类似于:
<p><img src="..."/>Some super text <i>here</></p>
因此它可以包含任何 html 输出、标题、div、手风琴等
此方法存储原始元素节点的副本(初始状态),其中文本搜索和突出显示应该发生了。
每次更改相关搜索字段的输入值时,都会触发一个全新的搜索过程,并且可能 matching/highlighting 在原始元素节点的另一个(总是新鲜的)副本中。
每个进程都从收集所有有效的文本节点开始。每个文本节点的 textContent
然后通过根据相关搜索字段的当前输入值创建的正则表达式获取 split
。
结果数组然后通过(聚合)渲染过程获得 reduce
d,该过程创建突出显示元素或纯文本节点,同时替换 recently/previously 处理的节点或被附加到后者...
// node detection helpers.
function isElementNode(node) {
return (node && (node.nodeType === 1));
}
function isNonEmptyTextNode(node) {
return (
node
&& (node.nodeType === 3)
&& (node.nodeValue.trim() !== '')
&& (node.parentNode.tagName.toLowerCase() !== 'script')
);
}
// dom node render helper.
function insertNodeAfter(node, referenceNode) {
const { parentNode, nextSibling } = referenceNode;
if (nextSibling !== null) {
node = parentNode.insertBefore(node, nextSibling);
} else {
node = parentNode.appendChild(node);
}
return node;
}
// 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
);
}
function getTextNodeList(rootNode) {
rootNode = (isElementNode(rootNode) && rootNode) || document.body;
const elementNodeList = Array.from(
rootNode.getElementsByTagName('*')
);
elementNodeList.unshift(rootNode);
return elementNodeList.reduce(collectTextNodeList, []);
}
// highlight functinality.
function createSearchMatch(text) {
const elmMatch = document.createElement('mark');
// elmMatch.classList.add("highlight");
elmMatch.textContent = text;
return elmMatch;
}
function aggregateSearchResult(collector, text, idx, arr) {
const { previousNode, regXSearch } = collector;
const currentNode = regXSearch.test(text)
? createSearchMatch(text)
: document.createTextNode(text);
if (idx === 0) {
previousNode.parentNode.replaceChild(currentNode, previousNode);
} else {
insertNodeAfter(currentNode, previousNode);
}
collector.previousNode = currentNode;
return collector;
}
function highlightSearch(textNode, regXSearch) {
// console.log(regXSearch);
textNode.textContent
.split(regXSearch)
.filter(text => text !== '')
.reduce(aggregateSearchResult, {
previousNode: textNode,
regXSearch,
})
}
function highlightSearchFromBoundContext(/* evt */) {
const { elmSearch, sourceNode, targetNode } = this;
const replacementNode = sourceNode.cloneNode(true);
const searchValue = elmSearch.value.trim();
if (searchValue !== '') {
const regXSearchString = searchValue
// from the OP's original code ... escaping of regex specific characters.
.replace((/[.*+?^${}()|[\]\]/g), '\$&')
// additional escaping of whitespace (sequences).
.replace((/\s+/g), '\s+');
const regXSearch = RegExp(`(${ regXSearchString })`, 'gi');
getTextNodeList(replacementNode).forEach(textNode =>
highlightSearch(textNode, regXSearch)
);
}
targetNode.parentNode.replaceChild(replacementNode, targetNode);
this.targetNode = replacementNode;
}
// initialize search behavior
function initializeSearchAndHighlight() {
const elmSearch = document
.querySelector('#block-multiblock-2 input[type="search"]');
const elmHighlight = elmSearch && document
.querySelector('.group-informacie .field-name-body');
if (elmHighlight && (elmHighlight.textContent.trim() !== '')) {
const handleChangeEvent = highlightSearchFromBoundContext.bind({
elmSearch,
targetNode: elmHighlight,
sourceNode: elmHighlight.cloneNode(true),
});
const handleChangeEventThrottled = _.throttle(handleChangeEvent, 200);
elmSearch.addEventListener('input', handleChangeEventThrottled);
handleChangeEvent();
}
}
initializeSearchAndHighlight();
p { margin: 7px 0 0 0; }
/*
.as-console-wrapper { max-height: 67px!important; }
*/
<label id="block-multiblock-2">
<span class="label">Highlight search ...</span>
<input
type="search"
placeholder="... type some text"
value="dolor (sit) amet"
/>
</label>
<article class="group-informacie">
<section class="field-name-body">
<p>
Lorem ipsum dolor (sit) amet, consetetur sadipscing elitr, ??sed?? diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam [erat], sed diam voluptua.
</p>
<p>
At vero [eos] et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, (no) **sea** takimata sanctus est Lorem ipsum dolor [sit] amet.
</p>
<p>
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
<p>
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
</p>
</section>
</article>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
促销
我的相关搜索和突出显示答案最近已经得到解答,可能有助于将此方法与类似问题...
- How to highlight the search-result of a text-query within an html document ignoring the html tags?
我正在使用以下代码来突出显示 div 中的文本。但是,如果我输入一些简单的东西,如“a”、“img”等,它会破坏 html 输出、图像并破坏网站。
if ($('#block-multiblock-2 input').val().length !== 0) {
$('.group-informacie .field-name-body p').each(function() {
//Handle special characters used in regex
var searchregexp = new RegExp($("#block-multiblock-2 input").val().replace(/[.*+?^${}()|[\]\]/g, '\$&'), "gi");
//$& will maintain uppercase and lowercase characters.
$(this).html($(this).html().replace(searchregexp, "<span class='highlight'>$&</span>"));
});
}
我认为问题在于必须以某种方式排除 html 标记的 RegExp?我尝试插入在其他问题中发现的 <> 左右的字符,但实际上没有任何效果。
我试图在用户/ckeditor 保存的文本中进行 jquery 搜索,输出有时类似于:
<p><img src="..."/>Some super text <i>here</></p>
因此它可以包含任何 html 输出、标题、div、手风琴等
此方法存储原始元素节点的副本(初始状态),其中文本搜索和突出显示应该发生了。
每次更改相关搜索字段的输入值时,都会触发一个全新的搜索过程,并且可能 matching/highlighting 在原始元素节点的另一个(总是新鲜的)副本中。
每个进程都从收集所有有效的文本节点开始。每个文本节点的 textContent
然后通过根据相关搜索字段的当前输入值创建的正则表达式获取 split
。
结果数组然后通过(聚合)渲染过程获得 reduce
d,该过程创建突出显示元素或纯文本节点,同时替换 recently/previously 处理的节点或被附加到后者...
// node detection helpers.
function isElementNode(node) {
return (node && (node.nodeType === 1));
}
function isNonEmptyTextNode(node) {
return (
node
&& (node.nodeType === 3)
&& (node.nodeValue.trim() !== '')
&& (node.parentNode.tagName.toLowerCase() !== 'script')
);
}
// dom node render helper.
function insertNodeAfter(node, referenceNode) {
const { parentNode, nextSibling } = referenceNode;
if (nextSibling !== null) {
node = parentNode.insertBefore(node, nextSibling);
} else {
node = parentNode.appendChild(node);
}
return node;
}
// 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
);
}
function getTextNodeList(rootNode) {
rootNode = (isElementNode(rootNode) && rootNode) || document.body;
const elementNodeList = Array.from(
rootNode.getElementsByTagName('*')
);
elementNodeList.unshift(rootNode);
return elementNodeList.reduce(collectTextNodeList, []);
}
// highlight functinality.
function createSearchMatch(text) {
const elmMatch = document.createElement('mark');
// elmMatch.classList.add("highlight");
elmMatch.textContent = text;
return elmMatch;
}
function aggregateSearchResult(collector, text, idx, arr) {
const { previousNode, regXSearch } = collector;
const currentNode = regXSearch.test(text)
? createSearchMatch(text)
: document.createTextNode(text);
if (idx === 0) {
previousNode.parentNode.replaceChild(currentNode, previousNode);
} else {
insertNodeAfter(currentNode, previousNode);
}
collector.previousNode = currentNode;
return collector;
}
function highlightSearch(textNode, regXSearch) {
// console.log(regXSearch);
textNode.textContent
.split(regXSearch)
.filter(text => text !== '')
.reduce(aggregateSearchResult, {
previousNode: textNode,
regXSearch,
})
}
function highlightSearchFromBoundContext(/* evt */) {
const { elmSearch, sourceNode, targetNode } = this;
const replacementNode = sourceNode.cloneNode(true);
const searchValue = elmSearch.value.trim();
if (searchValue !== '') {
const regXSearchString = searchValue
// from the OP's original code ... escaping of regex specific characters.
.replace((/[.*+?^${}()|[\]\]/g), '\$&')
// additional escaping of whitespace (sequences).
.replace((/\s+/g), '\s+');
const regXSearch = RegExp(`(${ regXSearchString })`, 'gi');
getTextNodeList(replacementNode).forEach(textNode =>
highlightSearch(textNode, regXSearch)
);
}
targetNode.parentNode.replaceChild(replacementNode, targetNode);
this.targetNode = replacementNode;
}
// initialize search behavior
function initializeSearchAndHighlight() {
const elmSearch = document
.querySelector('#block-multiblock-2 input[type="search"]');
const elmHighlight = elmSearch && document
.querySelector('.group-informacie .field-name-body');
if (elmHighlight && (elmHighlight.textContent.trim() !== '')) {
const handleChangeEvent = highlightSearchFromBoundContext.bind({
elmSearch,
targetNode: elmHighlight,
sourceNode: elmHighlight.cloneNode(true),
});
const handleChangeEventThrottled = _.throttle(handleChangeEvent, 200);
elmSearch.addEventListener('input', handleChangeEventThrottled);
handleChangeEvent();
}
}
initializeSearchAndHighlight();
p { margin: 7px 0 0 0; }
/*
.as-console-wrapper { max-height: 67px!important; }
*/
<label id="block-multiblock-2">
<span class="label">Highlight search ...</span>
<input
type="search"
placeholder="... type some text"
value="dolor (sit) amet"
/>
</label>
<article class="group-informacie">
<section class="field-name-body">
<p>
Lorem ipsum dolor (sit) amet, consetetur sadipscing elitr, ??sed?? diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam [erat], sed diam voluptua.
</p>
<p>
At vero [eos] et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, (no) **sea** takimata sanctus est Lorem ipsum dolor [sit] amet.
</p>
<p>
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
<p>
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
</p>
</section>
</article>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
促销
我的相关搜索和突出显示答案最近已经得到解答,可能有助于将此方法与类似问题...
- How to highlight the search-result of a text-query within an html document ignoring the html tags?