-
Notifications
You must be signed in to change notification settings - Fork 3.3k
第 90 题:实现模糊搜索结果的关键词高亮显示 #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
考虑节流、缓存。其实还可以上列表diff+定时清理缓存 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>auto complete</title>
<style>
bdi {
color: rgb(0, 136, 255);
}
li {
list-style: none;
}
</style>
</head>
<body>
<input class="inp" type="text">
<section>
<ul class="container"></ul>
</section>
</body>
<script>
function debounce(fn, timeout = 300) {
let t;
return (...args) => {
if (t) {
clearTimeout(t);
}
t = setTimeout(() => {
fn.apply(fn, args);
}, timeout);
}
}
function memorize(fn) {
const cache = new Map();
return (name) => {
if (!name) {
container.innerHTML = '';
return;
}
if (cache.get(name)) {
container.innerHTML = cache.get(name);
return;
}
const res = fn.call(fn, name).join('');
cache.set(name, res);
container.innerHTML = res;
}
}
function handleInput(value) {
const reg = new RegExp(`\(${value}\)`);
const search = data.reduce((res, cur) => {
if (reg.test(cur)) {
const match = RegExp.$1;
res.push(`<li>${cur.replace(match, '<bdi>$&</bdi>')}</li>`);
}
return res;
}, []);
return search;
}
const data = ["上海野生动物园", "上饶野生动物园", "北京巷子", "上海中心", "上海黄埔江", "迪士尼上海", "陆家嘴上海中心"]
const container = document.querySelector('.container');
const memorizeInput = memorize(handleInput);
document.querySelector('.inp').addEventListener('input', debounce(e => {
memorizeInput(e.target.value);
}))
</script>
</html> |
我的大概思路是,用正则替换掉关键词。 let panter = new RegExp(关键词, 'g')
该行字符串.replace(panter, '<b style="color: #2D7BFF">' + 关键词 + '</b>') ps:如果是vue项目,直接与v-html结合使用更爽哦~ |
|
直接把 输入的字符 变蓝, 服务端返回的数据 把输入字符替换掉,然后追加后面那部分 显示。 |
new RegExp 要注意排除有正则含义的字符,比如用户输入 |
用关键词去切分搜索结果为3段 let _head = item.substr(0, first) return _head + |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id='app'>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var myitem = {
template:`<div class='item'>
<p v-html="info.name"></p>
</div>`,
props:{
info:{
type:Object,
default:()=>{return {}},
}
}
}
var vm = new Vue({
el:'#app',
template:`
<div>
<input @input='inputSearchText'>
<myitem v-for='info in results' :key='info.name' :info='info'></item>
</div>
`,
data(){
return {
infos:[
{name:'地铁1',},
{name:'地铁6',},
{name:'地铁7',},
{name:'地铁10',},
{name:'地铁11',},
{name:'公交112',},
{name:'公交597',},
{name:'公交593',},
],
results:[],
}
},
created() {
this.results = JSON.parse(JSON.stringify(this.infos));
},
methods: {
inputSearchText : (function(timeout){
var timer;
return function(e){
if(timer){
clearTimeout(timer);
}
timer = setTimeout(() => {
this.search(e.target.value);
//this.search_text = e.target.value
}, timeout);
}
})(1000),
search(text){
var reg = RegExp(`(${text})`);
var results = JSON.parse(JSON.stringify(this.infos));
var matches = results.filter(info=>info.name.match(reg));
matches.forEach(info=>{
info.name = info.name.replace(reg,`<span class='highlight'>$1</span>`
)});
this.results = matches;
console.log(this.results);
}
},
components:{
myitem,
},
})
</script>
<style>
.highlight{
color:red;
}
</style>
</html> |
export function addMark(q, val) {
if (/^[\w\s\+\-]+$/.test(q)) {
let reg = q;
if (/(\s-|-\s)+[^\s-]/.test(q)) {
reg = q.split(/(\s-|-\s)/)[0];
} else if (/[\s+]+[^\s+]/.test(q)) {
reg = q.replace(/([\s+]+)/, '|');
}
reg = new RegExp(`(${reg})`,'igm');
return val.replace(reg,`<b>$1</b>`)
} else {
return val.replace(q,`<b>${q}</b>`)
}
} 个人网站在我看到此题前实现了一个,为纯英文时,需要注意不区分大小写.返回值通过vue v-html渲染
|
应用用节流还是防抖? |
debounce不是防抖么? |
mark一下,这个问题真的有意思 |
处理中文输入,再加个防抖。如果用数据驱动视图,可以“原地复用”,才是最优解。 <body>
<input id="input" type="text" />
<div id="box"></div>
<script>
let list = [
{ id: 1, name: "部门A", parentId: 0 },
{ id: 2, name: "部门B", parentId: 0 },
{ id: 3, name: "部门C", parentId: 1 },
{ id: 4, name: "部门D", parentId: 1 },
{ id: 5, name: "部门E", parentId: 2 },
{ id: 6, name: "部门F", parentId: 3 },
{ id: 7, name: "部门G", parentId: 2 },
{ id: 8, name: "部门H", parentId: 4 }
];
input.addEventListener("input", handleSearch);
input.addEventListener("compositionstart", handleStart);
input.addEventListener("compositionend", handleEnd);
function regLikeSearch(str) {
let htmls = "";
list.forEach(item => {
const match = item.name.replace(
new RegExp(str, "ig"),
(all, match) => {
return `<span style="color: red">${all}</span>`;
}
);
htmls += `<p>${match}</p>`;
});
box.innerHTML = htmls;
}
let isZhcn = false;
function handleSearch(e) {
if (!isZhcn) {
const val = e.target.value;
console.log("handleSearch", val);
regLikeSearch(val);
}
}
function handleStart(e) {
isZhcn = true;
}
function handleEnd(e) {
isZhcn = false;
handleSearch(e);
}
</script>
</body> |
|
v-html的xss攻击这样这样解决?
|
厉害,但是有点就是我看到实现的debounce是防抖,确实是使用防抖不能使用节流,只是最上面的文案是要修改一手。赞 |
let list = [
|
function a(str, childStr){ |
|
这不是防抖吗? @lhyt |
结合输入中文:compisitionsart compositionend
|
let panter = new RegExp(关键词, 'ig') |
在这里我有个问题,如果是富文本编辑器返回的内容,如何高亮选中,然后后端怎么在查找的时候,帅选掉那些标签呢 |
'变蓝变红变绿变蓝变'.match(/(变蓝)|((?!变蓝).)+/g) |
React 版本import { useEffect, useRef, useState } from "react";
const allData = ["asdew", "aedf", "123", "asdf"];
export default function App() {
const [inputVal, setInputVal] = useState("");
const data = useData(inputVal);
return (
<div className="App">
<input
value={inputVal}
onChange={(e) => {
const text = e.target.value;
setInputVal(text);
}}
/>
<ul>
{data.map((dataItem) => {
return (
<li>
{dataItem.split("").map((item) => {
return (
<span style={{ color: inputVal.includes(item) ? "red" : "" }}>
{item}
</span>
);
})}
</li>
);
})}
</ul>
</div>
);
}
const useData = (value) => {
const val = useDebounce(value, 500);
const [data, setData] = useState([]);
useEffect(() => {
if (!val) {
setData([]);
return;
}
setData(allData.filter((item) => item.includes(val)));
}, [val]);
return data;
};
const useDebounce = (value, interval = 1000) => {
const [data, setData] = useState(value);
const timer = useRef(null);
useEffect(() => {
clearTimeout(timer.current);
timer.current = setTimeout(() => {
setData(value);
}, interval);
return () => {
clearTimeout(timer.current);
};
}, [value, interval]);
return data;
}; |
|
需求上应该就只有两项:1、结果过滤;2、结果高亮。 <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>模糊搜索关键词高亮</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 30px;
}
.highlight {
background: yellow;
color: #d0021b;
}
.result-item {
margin: 8px 0;
}
input[type="text"] {
padding: 6px;
width: 240px;
font-size: 16px;
}
</style>
</head>
<body>
<h2>模糊搜索(关键词高亮)</h2>
<input type="text" id="searchInput" placeholder="请输入关键词..." />
<div id="results"></div>
<script>
// 静态数据
const data = [
'地铁线路图',
'地铁站',
'地铁9号线 曹路-松江南站',
'地铁2号线 徐泾东-广兰路',
'地铁11号线 迪士尼-嘉定北',
'地铁1号线 莘庄-富锦路',
'地铁10号线 新江湾城-虹桥火车站',
'南京东路(地铁站) 10号线 2号线',
'地铁3号线 上海南站-江杨北路',
'豫园(地铁站) 14号线(在建) 10号线'
];
const searchInput = document.getElementById('searchInput');
const resultsDiv = document.getElementById('results');
function highlightKeyword(text, keyword) {
if (!keyword) return text;
// 使用正则实现不区分大小写的高亮
const reg = new RegExp(`(${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return text.replace(reg, '<span class="highlight">$1</span>');
}
function renderResults(keyword) {
const filtered = data.filter(item => item.toLowerCase().includes(keyword.toLowerCase()));
if (filtered.length === 0) {
resultsDiv.innerHTML = '<div>无匹配结果</div>';
return;
}
resultsDiv.innerHTML = filtered.map(item =>
`<div class="result-item">${highlightKeyword(item, keyword)}</div>`
).join('');
}
// 初始渲染全部
renderResults('');
searchInput.addEventListener('input', e => {
renderResults(e.target.value.trim());
});
</script>
</body>
</html> |
Uh oh!
There was an error while loading. Please reload this page.
The text was updated successfully, but these errors were encountered: