[英]Search and highlight text on page while keeping html structure
可能已經有人問過類似的問題,但請仔細閱讀詳細信息。
我正在使用一種自制的自動完成功能,現在我想突出顯示結果集中的搜索詞。 到目前為止,這有效,但僅適用於純文本。 問題是:如果結果div中有一個,我需要保留html結構。 請參閱我的示例:目前我正在丟失包含粗體的跨度。 我怎樣才能留住它們?
感謝您的任何建議!
$('#box').keyup(function () { const valThis = this.value; const length = this.value.length; $('.objType').each(function () { const text = $(this).text(); const textL = text.toLowerCase(); const position = textL.indexOf(valThis.toLowerCase()); if (position !== -1) { const matches = text.substring(position, (valThis.length + position)); const regex = new RegExp(matches, 'ig'); const highlighted = text.replace(regex, `<mark>${matches}</mark>`); $(this).html(highlighted).show(); } else { $(this).text(text); $(this).hide(); } }); });
input[type="text"] { width: 50%; margin:10px; padding: 5px; float:left; clear:left; } div{ float:left; clear:left; margin:10px 10px; } .bold { font-weight: 700; } table td { border: solid 1px #ccc; padding: 3px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script> <input placeholder="Filter results" id="box" type="text" /> <div class="objType" id="item1"> <span class="bold">Accepted</span> Event Relation <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item2"> Case <span class="bold">Status</span> Value <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item3"> External <span class="bold">Data Source</span> <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item4"> Navigation <span class="bold">Link</span> Set <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div>
PS:另外一個 JSFiddle 可能會有所幫助 => https://jsfiddle.net/SchweizerSchoggi/6x3ak5d0/7/
使用mark.js
插件
$('#box').on('input', function() { const valThis = this.value; const options = { filter: function(node, term, totalCounter, counter) { $(node).parents('.objType').show() return true } }; $('.objType').unmark({ done: function() { $('.objType') .toggle(valThis.length === 0) .mark(valThis, options); } }) });
input[type="text"] { width: 50%; margin: 10px; padding: 5px; float: left; clear: left; } div { float: left; clear: left; margin: 10px 10px; } .bold { font-weight: 700; } table td { border: solid 1px #ccc; padding: 3px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/jquery.mark.min.js" integrity="sha256-4HLtjeVgH0eIB3aZ9mLYF6E8oU5chNdjU6p6rrXpl9U=" crossorigin="anonymous"></script> <input placeholder="Filter results" id="box" type="text" /> <div class="objType" id="item1"> <span class="bold">Accepted</span> Event Relation <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item2"> Case <span class="bold">Status</span> Value <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item3"> External <span class="bold">Data Source</span> <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item4"> Navigation <span class="bold">Link</span> Set <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div>
這是僅使用本機 javascript的可能基礎。 這有點像CTRL + F 。
這似乎保留了<td>
元素。
clear 函數用wbr
元素替換mark
元素:
在 UTF-8 編碼的頁面上,
<wbr>
行為類似於 U+200B 零寬度空間代碼點。 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr
function mark(it){ clearIt() if (it.length > 2){ let c = new RegExp(it, "ig") main.innerHTML = main.innerHTML.replace(c,"<mark>"+it+"</mark>") } } function clearIt(){ let b = new RegExp("mark>", "ig") main.innerHTML = main.innerHTML.replace(b,"wbr>") } mark(search.value)
input[type="text"] { width: 50%; margin:10px; padding: 5px; float:left; clear:left; } div{ float:left; clear:left; margin:10px 10px; } .bold { font-weight: 700; } table td { border: solid 1px #ccc; padding: 3px; }
<input onfocusout="clearIt()" oninput="mark(this.value)" value="Lorem" id="search" placeholder="Lorem"> <button onclick="mark(search.value)">SEARCH</button> <button onclick="clearIt()">CLEAR</button> <div id="main"> <div class="objType" id="item1"> <span class="bold">Accepted</span> Event Relation <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item2"> Case <span class="bold">Status</span> Value <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item3"> External <span class="bold">Data Source</span> <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item4"> Navigation <span class="bold">Link</span> Set <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> </div>
順便說一下,恢復/清除的標記不是原始標記,要完全恢復它,您可能需要在標記之前復制整個 html。
為簡單起見,我在 vue 文件中創建了該功能(因為這些功能易於實現且變量易於插入):
<template>
<div class="container">
<div class="position-relative">
<input
v-model="inputValue"
type="search"
autocomplete="off"
class="form-control"
@input="onInput" />
</div>
<pre v-html="results" />
</div>
</template>
<script>
export default {
name: 'Typeahead',
data() {
return {
inputValue: '',
items: [
{ value: '' },
{ value: '' },
{ value: '' },
// and so on (Value is the field to be searched).
],
results: [],
};
},
created() {
this.results = this.items; // initially assign all the items as results
},
methods: {
onInput() { // input event (use event target value for vanilla js.)
const value = this.inputValue;
if (!value) {
this.results = this.items;
return;
}
const escapedQuery = this.escapeRegExp(value); // escape any special characters
const queryReg = new RegExp(escapedQuery, 'gi'); // create a regular expression out of it
this.results = this.matchItem(this.items, queryReg) // call match function
.map(item => ({
...item,
value: this.highlight(item.value, queryReg), // highlight the matched text range
}));
},
escapeHtml(text) {
return text.replace(/</g, '<').replace(/>/g, '>');
},
escapeRegExp(text) {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
},
matchItem(items, queryReg) {
return items
.filter(i => i.value.match(queryReg) !== null)
.sort((a, b) => {
const aIndex = a.value.indexOf(a.value.match(queryReg)[0]);
const bIndex = b.value.indexOf(b.value.match(queryReg)[0]);
if (aIndex < bIndex) return -1;
if (aIndex > bIndex) return 1;
return 0;
});
},
highlight(text, queryReg) {
const escaped = this.escapeHtml(text);
return escaped.replace(queryReg, '<b>$&</b>');
},
},
};
</script>
基本上,它所做的是從輸入文本中轉義任何特殊符號並從中創建一個正則表達式,然后過濾掉與該正則表達式匹配的記錄。 然后根據匹配的強度對元素進行排序。 現在,匹配的部分(記錄中的值)使用strong
或b
html 標簽(我在這里使用了粗體標簽)突出顯示,它可以輕松嵌入到 html 中,按預期產生突出顯示的輸出。
我使用了 pre 標簽來顯示結果。 您可以根據需要創建表結構。
這些方法在 vanilla javascript 中,並且沒有太多的 vue 進行(除了this
參考)。
我希望它有幫助:)
我試圖研究你的方法,並提供了以下解決方案。 在大多數情況下它工作正常。
/* This function will get all the indices of searched term in the html in array format eg if html is <span>Accept</span> and user types a Input: getAllIndicesOf(a, "<span>Accept</span>", false) Output: [3,6,16] */ function getAllIndicesOf(searchStr, str, caseSensitive) { var searchStrLen = searchStr.length; if (searchStrLen == 0) { return []; } var startIndex = 0, index, indices = []; if (!caseSensitive) { str = str.toLowerCase(); searchStr = searchStr.toLowerCase(); } while ((index = str.indexOf(searchStr, startIndex)) > -1) { indices.push(index); startIndex = index + searchStrLen; } return indices; } /* What ever values I am getting from getAllIndicesOf, here I try to find if the searched value is not present inside html tag. eg if html is <span>Accept</span> and user types a getAllIndicesOf will output [3,6,16] Input: findValidMatches([3,6,16], "a") Output: [6] Logic: The valid matching text will lie between > and <. If it lies between < and >, it is a html tag. */ function findValidMatches(pseudoPosition, str) { const pos = [] for (let i = 0; i<pseudoPosition.length; ++i) { const splitText = str.substr(pseudoPosition[i]) const indexOfLT = splitText.indexOf("<") const indexOfGT = splitText.indexOf(">") if (indexOfLT > -1 && indexOfGT > -1 && indexOfLT < indexOfGT) { pos.push(pseudoPosition[i]) } else if (indexOfLT === -1 && indexOfGT > -1 && indexOfGT < 0) { pos.push(pseudoPosition[i]) } else if (indexOfGT === -1 && indexOfLT > -1 && indexOfLT > 0) { pos.push(pseudoPosition[i]) } else if (indexOfLT === -1 && indexOfGT === -1) { pos.push(pseudoPosition[i]) } } return pos } /* This will replace the matched valid string with <mark>text</mark> to highlight if html is <span>Accept</span> and user types a getAllIndicesOf will output [3,6,16] -> findValidMatches will output [6] -> input to replaceText replaceText("<span>Accept</span>", [6], "a") will output <span><mark>A</mark></span> */ function replaceText(text, correctPositions, valueToReplace) { let copyText = text for (let i = 0; i<correctPositions.length; ++i) { const upValue = correctPositions[i] + 13*i const firstPart = copyText.slice(0, upValue) const lastPart = copyText.slice(upValue + valueToReplace.length, copyText.length) const valueWithCase = copyText.substr(upValue, valueToReplace.length) copyText = firstPart + "<mark>" + valueWithCase +"</mark>" + lastPart } return copyText } $('#box').keyup(function () { const valThis = this.value; $('.objType').each(function () { const text = $(this).html().replace(/<mark>/gi, "").replace(/<\\/mark>/gi, ""); const position = getAllIndicesOf(valThis, text) //Get all indices of valThis in the html const correctPositions = findValidMatches(position, text) //Filter only those indices which indicate that they are text and not html const updatedText = replaceText(text, correctPositions, valThis) //Get the updated text with mark tags if (correctPositions.length > 0) { $(this).html(updatedText) $(this).show(); } else { if (valThis.length > 0) $(this).hide(); else { $(this).html(text) $(this).show(); } } }); });
input[type="text"] { width: 50%; margin:10px; padding: 5px; float:left; clear:left; } div{ float:left; clear:left; margin:10px 10px; } .bold { font-weight: 700; } table td { border: solid 1px #ccc; padding: 3px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <input placeholder="Filter results" id="box" type="text" /> <div class="objType" id="item1"> <span class="bold">Accepted</span> Event Relation <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item2"> Case <span class="bold">Status</span> Value <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item3"> External <span class="bold">Data Source</span> <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div> <div class="objType" id="item4"> Navigation <span class="bold">Link</span> Set <table> <tr> <td>Lorem</td> <td>ipsum</td> </tr> </table> </div>
我遵循的方法是直接搜索html部分然后更新。
方法
const text = $(this).html().replace(/<mark>/gi, "").replace(/<\\/mark>/gi, "");
)getAllIndicesOf
)findValidMatches
)<mark></mark>
標簽來重建 html(使用replaceText
) 可能會有很多相關的問題(例如,如果有事件處理程序,如果您嘗試搜索帶有 html 標簽的文本,例如嘗試搜索Accepted
Event
,則搜索將失敗)。 我會嘗試更新。
希望能幫助到你。
遵循@Sunil 解決方案導致問題的原因是您需要刪除它的<span>
標簽,所以我編輯了這部分代碼:
$('.objType').each(function () {
const text = $(this).html().replace(/<mark>/gi, "").replace(/<\/mark>/gi, "");
const text2 = text.replace(/<span class=\"bold\">/gi, "").replace(/<\/span>/gi, "");
const position = getAllIndicesOf(valThis, text2) //Get all indices of valThis in the html
const correctPositions = findValidMatches(position, text2) //Filter only those indices which indicate that they are text and not html
const updatedText = replaceText(text2, correctPositions, valThis) //Get the updated text with mark tags
if (correctPositions.length > 0) {
$(this).html(updatedText)
$(this).show();
} else {
if (valThis.length > 0) $(this).hide();
else {
$(this).html(text2)
$(this).show();
}
}
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.