You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 10:00:02