diff --git a/src/views/common/DocumentSearchWord.vue b/src/views/common/DocumentSearchWord.vue index 4f31288..5e1e505 100644 --- a/src/views/common/DocumentSearchWord.vue +++ b/src/views/common/DocumentSearchWord.vue @@ -176,6 +176,33 @@ export default { return (element.textContent || '').trim() }, + getSearchableTextContent(element) { + if (!element) return '' + let text = '' + const walker = document.createTreeWalker( + element, + NodeFilter.SHOW_TEXT, + { + acceptNode: (node) => { + const parent = node.parentElement + if (!parent) return NodeFilter.FILTER_REJECT + const tagName = parent.tagName ? parent.tagName.toLowerCase() : '' + if (tagName === 'mark' || tagName === 'script' || tagName === 'style' || tagName === 'noscript') { + return NodeFilter.FILTER_REJECT + } + return NodeFilter.FILTER_ACCEPT + } + } + ) + let node + while ((node = walker.nextNode())) { + if (node.textContent) { + text += node.textContent + } + } + return text + }, + prepareSearchSegments(forceReset = false) { if (!this.docRendered) { if (forceReset) { @@ -270,7 +297,7 @@ export default { highlightTextInNode(node, pattern, results, segmentIndex, parentElement, markIndexRef) { if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent - if (!text || !text.trim()) return + if (!text) return pattern.lastIndex = 0 const textMatches = [] @@ -355,17 +382,9 @@ export default { const results = [] let totalTextMatches = 0 + const segmentMatchCounts = [] this.searchSegments.forEach((el, segmentIndex) => { - const originalText = el.dataset.originalText - if (typeof originalText === 'undefined' || !originalText) return - - pattern.lastIndex = 0 - const textMatchCount = (originalText.match(pattern) || []).length - if (textMatchCount === 0) return - - totalTextMatches += textMatchCount - const originalHtml = el.dataset.originalHtml if (typeof originalHtml === 'undefined') return @@ -373,25 +392,47 @@ export default { el.innerHTML = cleanHtml el.dataset.originalHtml = cleanHtml + const searchableText = this.getSearchableTextContent(el) + if (!searchableText) return + + pattern.lastIndex = 0 + const textMatchCount = (searchableText.match(pattern) || []).length + if (textMatchCount === 0) return + + totalTextMatches += textMatchCount + const existingMarks = el.querySelectorAll('mark') if (existingMarks.length > 0) { console.warn(`Segment ${segmentIndex} still has ${existingMarks.length} mark tags after cleaning`) } const markIndexRef = { value: 0 } + const beforeHighlightText = this.getSearchableTextContent(el) const children = Array.from(el.childNodes) children.forEach(child => { this.highlightTextInNode(child, pattern, results, segmentIndex, el, markIndexRef) }) const createdMarks = el.querySelectorAll('mark.search-highlight') - if (createdMarks.length !== textMatchCount) { - console.warn(`Segment ${segmentIndex}: expected ${textMatchCount} marks, created ${createdMarks.length}`) + const actualCount = createdMarks.length + const afterHighlightText = this.getSearchableTextContent(el) + + segmentMatchCounts.push({ + segmentIndex, + expected: textMatchCount, + actual: actualCount, + searchableTextLength: searchableText.length, + beforeHighlightLength: beforeHighlightText.length, + afterHighlightLength: afterHighlightText.length + }) + + if (actualCount !== textMatchCount) { + console.warn(`Segment ${segmentIndex}: expected ${textMatchCount} marks, created ${actualCount}. Text: "${searchableText.substring(0, 100)}..."`) } }) if (results.length !== totalTextMatches) { - console.warn(`Total matches mismatch: expected ${totalTextMatches}, got ${results.length}`) + console.warn(`Total matches mismatch: expected ${totalTextMatches}, got ${results.length}`, segmentMatchCounts) } this.searchResults = results