如何使用JavaScript根据输入关键词高亮完整匹配文本
问题根因
- 异步状态更新时序问题:
await search(e.target.value, setRows)执行后,setRows是React异步更新状态的API,调用后不会立刻更新DOM,此时立刻执行高亮逻辑,操作的还是旧的搜索结果DOM,自然匹配不到新返回的完整词汇。 - 高亮逻辑存在历史标签干扰:每次输入时都会给匹配到的部分内容套上
<b>标签,后续输入完整词汇时,DOM结构中已经存在嵌套的标签,原始文本被拆分,无法匹配到完整的连续字符串。 - 选择器错误:DOM中id是唯一属性,使用
document.querySelectorAll("#search")只能获取到一个元素,无法批量处理多个搜索结果项。 - 匹配逻辑缺陷:单次replace只能替换第一个匹配到的内容,无法实现全局匹配,且没有处理搜索词中的正则特殊字符,容易出现匹配异常。
修复方案
1. 修改输入事件逻辑,移除同步高亮调用
const handleOnChange = async (e) => { const inputVal = e.target.value; setSearchText(inputVal); if (inputVal.length <= 0) { setSearchSuggestion(true); setRows([]); } else { setSearchSuggestion(false); await search(inputVal, setRows); } };
2. 新增useEffect监听状态变化,确保DOM渲染完成后执行高亮
import { useEffect } from 'react'; // 搜索词、搜索结果更新后自动触发高亮 useEffect(() => { if (!searchText || rows.length === 0) return; // 延迟0ms执行,等待React完成DOM渲染 const timer = setTimeout(() => handleSearchHighliter(searchText), 0); return () => clearTimeout(timer); }, [searchText, rows]);
3. 重写高亮逻辑,解决历史标签干扰和匹配问题
给所有需要高亮的搜索结果项统一添加class search-item,替换原有的id选择器:
const handleSearchHighliter = (str) => { // 转义搜索词中的正则特殊字符 const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const matchReg = new RegExp(`(${escapeRegExp(str)})`, 'gi'); const searchItems = [...document.querySelectorAll(".search-item")]; searchItems.forEach(item => { // 先获取纯文本清空历史高亮标签,避免标签干扰匹配 const pureText = item.innerText; if (pureText.toLowerCase().includes(str.toLowerCase())) { item.innerHTML = pureText.replace(matchReg, `<b style="background-color: yellow">$1</b>`); } }); };
更优的无DOM操作实现方案
直接在渲染搜索结果时处理高亮,避免原生DOM操作和React状态不同步的问题:
// 封装高亮文本组件 const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const HighlightText = ({ text, keyword }) => { if (!keyword) return <>{text}</>; const splitReg = new RegExp(`(${escapeRegExp(keyword)})`, 'gi'); const textParts = text.split(splitReg); return <> {textParts.map((part, index) => part.toLowerCase() === keyword.toLowerCase() ? <b key={index} style={{backgroundColor: 'yellow'}}>{part}</b> : part )} </>; }; // 渲染搜索结果时直接调用 {rows.map((row, index) => ( <div key={index} className="search-item"> <HighlightText text={row.content} keyword={searchText} /> </div> ))}
内容的提问来源于stack exchange,提问作者Md Rezaul Karim
相关产品推荐
相关产品推荐

