如何实现可同时匹配<a>链接与纯文本内容的列表过滤?
I’ve run into similar headaches before—combining link and plain text matching while keeping HTML entities intact can be tricky, but here’s a robust approach tailored exactly to your needs:
Core Problem Breakdown
Your current filter works for either the <a> text or the trailing plain text, but not their combined content. On top of that, innerText breaks HTML entity recognition because it parses entities into their rendered characters (like turning & into &), which can mismatch user input that includes raw entity strings.
Step-by-Step Solution
We’ll build a filter that:
- Grabs raw text content including HTML entities (ditching
innerTextentirely) - Handles both comma-separated and space-separated user queries seamlessly
- Matches combinations of link text and trailing plain text
Code Implementation
First, here’s a sample list to test with:
<ul id="locationList"> <li><a href='#.html'>San Carlos</a>, city</li> <li><a href='#.html'>Los Angeles</a>, city</li> <li><a href='#.html'>San Diego & County</a>, town</li> <li><a href='#.html'>Mountain View</a>, village</li> </ul>
Now the filtering function:
function filterLocationList(userQuery) { const listItems = document.querySelectorAll('#locationList li'); // Show all items if query is empty if (!userQuery.trim()) { listItems.forEach(item => item.style.display = ''); return; } // Normalize query to match both space-separated and comma-separated formats const normalizedQuery = userQuery.trim().replace(/(\s+|,\s+)/g, '\\s*(?:,\\s*)?'); // Escape special regex characters to avoid syntax errors const escapedQuery = normalizedQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Case-insensitive regex for flexible matching const matchRegex = new RegExp(escapedQuery, 'i'); listItems.forEach(item => { // Extract raw text (including HTML entities) by stripping tags from innerHTML const rawItemText = item.innerHTML.replace(/<[^>]+>/g, '').trim(); // Toggle visibility based on match item.style.display = matchRegex.test(rawItemText) ? '' : 'none'; }); } // Example calls: filterLocationList('san carlos city'); // Shows the San Carlos city item filterLocationList('san carlos, city'); // Also shows the San Carlos city item filterLocationList('san diego & county town'); // Matches the San Diego & County town item
Key Details Explained
- Avoiding
innerText: By usinginnerHTMLand stripping tags, we retain raw HTML entities exactly as they appear in the DOM. This means user input containing entities (like&) will match correctly, whereasinnerTextwould have parsed it to&and broken the match. - Query Normalization: The regex pattern we build allows matching both space-separated (
san carlos city) and comma-separated (san carlos, city) queries against the list item’s formatted text (San Carlos, city). - Edge Case Handling: We added a check for empty queries to show all items, and escaped regex special characters to prevent crashes if the user inputs characters like
*or?.
Alternative: Granular Control Over Link + Trailing Text
If you need to prioritize matching the link text first or handle each segment separately, use this version:
function filterLocationList(userQuery) { const listItems = document.querySelectorAll('#locationList li'); const normalizedQuery = userQuery.trim().replace(/(\s+|,\s+)/g, '\\s*(?:,\\s*)?'); const escapedQuery = normalizedQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const matchRegex = new RegExp(escapedQuery, 'i'); listItems.forEach(item => { const linkElement = item.querySelector('a'); let fullText = ''; if (linkElement) { // Get raw link text (preserving entities) const rawLinkText = linkElement.innerHTML.trim(); // Get raw trailing text (preserving entities) const rawTrailingText = item.innerHTML.replace(linkElement.outerHTML, '').trim(); fullText = `${rawLinkText} ${rawTrailingText}`; } else { // Fallback for items without links fullText = item.innerHTML.replace(/<[^>]+>/g, '').trim(); } item.style.display = matchRegex.test(fullText) ? '' : 'none'; }); }
This version gives you precise control over each text segment while still preserving HTML entities.
内容的提问来源于stack exchange,提问作者JAT86

