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

匹配高亮时函数无法正确显示HTML标签格式的问题求助

字符串匹配高亮导致HTML格式破坏的解决方法

问题描述

我尝试实现字符串匹配高亮功能,但开启高亮代码element.innerHTML = highlightedText;后,div#results区域内的UL/LI原有格式无法正常显示;注释掉该代码后,文本显示正常但没有高亮效果。求解决思路。

原代码参考

JavaScript代码

function wrapMatchesInSpan(element, searchTerm) {
  var text = element.textContent;
  var regex = new RegExp(searchTerm, 'gi');
  var matches = text.match(regex);
  if (matches && matches.length > 0) {
    var highlightedText = text.replace(regex, function(match) {
      return '<span class="highlight">' + match + '</span>';
    });
    element.innerHTML = highlightedText; //if I comment this line, then the text works but, without format
  }
}

function search() {
  var searchTerm = document.getElementById("searchInput").value.toLowerCase();
  var listItems = document.getElementById("searchResults").getElementsByTagName("li");
  var teamItems = document.getElementById("searchTeams").getElementsByTagName("li");
  var resultsDiv = document.getElementById("results");

  for (var i = 0; i < listItems.length; i++) {
    var listItem = listItems[i];
    var text = listItem.textContent.toLowerCase();

    if (text.includes(searchTerm)) {
      listItem.innerHTML = listItem.innerHTML.replace(new RegExp(searchTerm, 'gi'), function(match) {
        return '<span class="highlight">' + match + '</span>';
      });
      listItem.style.display = "list-item";
    } else {
      listItem.innerHTML = ''; // Clear the content
      listItem.style.display = "none";
    }
  }

  var hideAllTeams = true;
  for (var j = 0; j < teamItems.length; j++) {
    var teamItem = teamItems[j];
    var teamText = teamItem.textContent.toLowerCase();

    if (teamText.includes(searchTerm)) {
      teamItem.style.display = "list-item";
      hideAllTeams = false;
    } else {
      teamItem.style.display = "none";
    }
  }

  if (searchTerm === "") {
    resultsDiv.style.display = "none";
  } else {
    resultsDiv.style.display = "block";
  }

  if (hideAllTeams) {
    teamsDiv.style.display = "none";
  } else {
    teamsDiv.style.display = "block";
  }
}

CSS代码

* {
  box-sizing: border-box;
}

#results {
  display: none;
  margin-top: 10px;
}

#searchResults {
  background-color: #f6f6f6;
  margin-top: -2px;
  width: 430px;
  height: 200px;
  border: 1px solid #ddd;
}

#searchInput {
  background-image: url('/css/searchicon.png');
  background-position: 10px 12px;
  background-repeat: no-repeat;
  width: 425px;
  font-size: 14px;
  padding: 12px 20px 12px 40px;
  border: 1px solid #ddd;
  margin-bottom: 12px;
}

#searchTeams {
  list-style-type: none;
  width: 425px;
  font-size: 16px;
  border: 1px solid #ddd;
}

#searchTeams li a {
  color: 1px solid #ddd;
  margin-top: -2px;
  /* Prevent double borders */
  background-color: #f6f6f6;
  background-position: 10px 12px;
  font-weight: bold;
  padding: 12px;
  text-decoration: none;
  font-size: 19px;
  color: black;
  display: block
}

#teams {
  color: 1px solid #ddd;
  margin-top: -2px;
  /* Prevent double borders */
  padding: 12px;
  text-decoration: none;
}

#searchTeams li a:hover:not(.header) {
  background-color: #aeadca;
  width: 100%;
  padding-left: 35px;
  color: blue;
}

ul.scroll {
  background-color: #f6f6f6;
  width: 600px;
  height: 500px;
  overflow-x: hidden;
  overflow-y: auto;
  text-align: left;
  padding: 20px;
}

.highlight {
  background-color: yellow;
}

HTML代码

<!DOCTYPE html>
<html>

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>NACC-Teams-Locator</title>
</head>

<body>
  <div id="teams">
    <h2>ACC Teams:</h2>
    <input type="text" id="searchInput" placeholder="Search for teams / keywords / services..." oninput="search()">
    <ul class="scroll" id="searchTeams">
      <li style="display: list-item;"><a href="#">WMAA</a></li>
      <li style="display: list-item;"><a href="#">MobileInf</a></li>
      <li style="display: list-item;"><a href="#">MSM</a></li>
      <li style="display: list-item;"><a href="#">SECAPPS</a></li>
      <li style="display: list-item;"><a href="#">SECAPPS</a></li>
    </ul>
    <div id="results">
      <h3>Services supported by team:</h3>
      <ul id="searchResults" class="scroll">
        <li style="display: none;">
          <h3>MobileInf:</h3><br> -APIs<br> -Layer 7<br> -Gloo<br> -Gateways<br> -API portal</li>
        <li style="display: none;">
          <h3>Midrange Servers Management (MSM):</h3> -Servers<br> -Filesystem usage<br> -Patching<br> -Root<br> -API portal</li>
        <li style="display: none;">
          <h3>SECAPPS:</h3> -Okta<br> -Passwordsafe<br> -OIG<br> -Forgerock<br> -Evidian <br> -Cyberark<br> -go/password</li>
        <li style="display: none;">
          <h3>Hadoop:</h3> -Hive<br> -Cloudera<br> -Hue/Ambari<br> -Yarn/HDFS<br> -Hadoop Data Movement</b>
        </li>
      </ul>
    </div>
  </div>
  <p>
    //<img src="" />
  </p>
</body>

</html>

问题根源

  1. 直接替换innerHTML会破坏原有HTML结构(比如LI内的<h3>标签会被当成文本处理,或重复替换导致嵌套<span>)
  2. 每次搜索未重置之前的高亮状态,导致多次搜索后HTML结构混乱
  3. 代码中teamsDiv未定义,会引发控制台错误

修复方案

1. 保存原始HTML内容

给需要高亮的列表项添加自定义属性data-original,存储初始HTML内容,每次搜索前先恢复原始状态。

2. 针对文本节点进行高亮替换

遍历元素的文本节点,只在纯文本中匹配搜索词并包裹高亮标签,避免修改现有HTML标签结构。

3. 修复未定义变量

提前获取teamsDiv DOM元素,避免报错。


修改后的完整代码

JavaScript代码

// 仅在文本节点中包裹匹配的搜索词,不破坏HTML结构
function highlightTextNodes(element, searchTerm) {
  if (!searchTerm.trim()) return;
  
  const regex = new RegExp(searchTerm, 'gi');
  const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
  let node;

  while (node = walker.nextNode()) {
    if (node.parentElement.classList.contains('highlight')) continue; // 跳过已高亮的节点
    const text = node.textContent;
    const matches = text.match(regex);
    if (matches) {
      const fragments = text.split(regex);
      const parent = node.parentElement;
      parent.removeChild(node);
      
      fragments.forEach((fragment, index) => {
        if (fragment) parent.appendChild(document.createTextNode(fragment));
        if (index < matches.length) {
          const span = document.createElement('span');
          span.className = 'highlight';
          span.textContent = matches[index];
          parent.appendChild(span);
        }
      });
    }
  }
}

function search() {
  const searchTerm = document.getElementById("searchInput").value.toLowerCase().trim();
  const listItems = document.getElementById("searchResults").getElementsByTagName("li");
  const teamItems = document.getElementById("searchTeams").getElementsByTagName("li");
  const resultsDiv = document.getElementById("results");
  const teamsDiv = document.getElementById("teams"); // 修复未定义变量

  // 重置服务列表项的状态
  for (let i = 0; i < listItems.length; i++) {
    const listItem = listItems[i];
    // 恢复原始HTML内容
    listItem.innerHTML = listItem.dataset.original || listItem.innerHTML;
    const text = listItem.textContent.toLowerCase();

    if (searchTerm && text.includes(searchTerm)) {
      highlightTextNodes(listItem, searchTerm);
      listItem.style.display = "list-item";
    } else {
      listItem.style.display = "none";
    }
  }

  // 处理团队列表项
  let hideAllTeams = true;
  for (let j = 0; j < teamItems.length; j++) {
    const teamItem = teamItems[j];
    const link = teamItem.querySelector('a');
    // 恢复原始文本
    link.textContent = link.dataset.original || link.textContent;
    const teamText = teamItem.textContent.toLowerCase();

    if (searchTerm && teamText.includes(searchTerm)) {
      highlightTextNodes(link, searchTerm);
      teamItem.style.display = "list-item";
      hideAllTeams = false;
    } else {
      teamItem.style.display = "none";
    }
  }

  // 控制结果区域显示
  resultsDiv.style.display = searchTerm ? "block" : "none";
  teamsDiv.style.display = hideAllTeams && searchTerm ? "none" : "block";
}

// 页面加载时保存原始内容
document.addEventListener('DOMContentLoaded', function() {
  // 保存服务列表的原始HTML
  const serviceItems = document.getElementById("searchResults").getElementsByTagName("li");
  for (let item of serviceItems) {
    item.dataset.original = item.innerHTML;
  }
  // 保存团队链接的原始文本
  const teamLinks = document.querySelectorAll("#searchTeams li a");
  for (let link of teamLinks) {
    link.dataset.original = link.textContent;
  }
});

CSS代码(无修改)

* {
  box-sizing: border-box;
}

#results {
  display: none;
  margin-top: 10px;
}

#searchResults {
  background-color: #f6f6f6;
  margin-top: -2px;
  width: 430px;
  height: 200px;
  border: 1px solid #ddd;
}

#searchInput {
  background-image: url('/css/searchicon.png');
  background-position: 10px 12px;
  background-repeat: no-repeat;
  width: 425px;
  font-size: 14px;
  padding: 12px 20px 12px 40px;
  border: 1px solid #ddd;
  margin-bottom: 12px;
}

#searchTeams {
  list-style-type: none;
  width: 425px;
  font-size: 16px;
  border: 1px solid #ddd;
}

#searchTeams li a {
  color: 1px solid #ddd;
  margin-top: -2px;
  /* Prevent double borders */
  background-color: #f6f6f6;
  background-position: 10px 12px;
  font-weight: bold;
  padding: 12px;
  text-decoration: none;
  font-size: 19px;
  color: black;
  display: block
}

#teams {
  color: 1px solid #ddd;
  margin-top: -2px;
  /* Prevent double borders */
  padding: 12px;
  text-decoration: none;
}

#searchTeams li a:hover:not(.header) {
  background-color: #aeadca;
  width: 100%;
  padding-left: 35px;
  color: blue;
}

ul.scroll {
  background-color: #f6f6f6;
  width: 600px;
  height: 500px;
  overflow-x: hidden;
  overflow-y: auto;
  text-align: left;
  padding: 20px;
}

.highlight {
  background-color: yellow;
}

HTML代码(无修改)

<!DOCTYPE html>
<html>

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>NACC-Teams-Locator</title>
</head>

<body>
  <div id="teams">
    <h2>ACC Teams:</h2>
    <input type="text" id="searchInput" placeholder="Search for teams / keywords / services..." oninput="search()">
    <ul class="scroll" id="searchTeams">
      <li style="display: list-item;"><a href="#">WMAA</a></li>
      <li style="display: list-item;"><a href="#">MobileInf</a></li>
      <li style="display: list-item;"><a href="#">MSM</a></li>
      <li style="display: list-item;"><a href="#">SECAPPS</a></li>
      <li style="display: list-item;"><a href="#">SECAPPS</a></li>
    </ul>
    <div id="results">
      <h3>Services supported by team:</h3>
      <ul id="searchResults" class="scroll">
        <li style="display: none;">
          <h3>MobileInf:</h3><br> -APIs<br> -Layer 7<br> -Gloo<br> -Gateways<br> -API portal</li>
        <li style="display: none;">
          <h3>Midrange Servers Management (MSM):</h3> -Servers<br> -Filesystem usage<br> -Patching<br> -Root<br> -API portal</li>
        <li style="display: none;">
          <h3>SECAPPS:</h3> -Okta<br> -Passwordsafe<br> -OIG<br> -Forgerock<br> -Evidian <br> -Cyberark<br> -go/password</li>
        <li style="display: none;">
          <h3>Hadoop:</h3> -Hive<br> -Cloudera<br> -Hue/Ambari<br> -Yarn/HDFS<br> -Hadoop Data Movement</b>
        </li>
      </ul>
    </div>
  </div>
  <p>
    //<img src="" />
  </p>
</body>

</html>

内容的提问来源于stack exchange,提问作者Oswaldo Barajas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 05:44:55