// ==UserScript== // @name BZ综合网页搜索脚本 // @namespace http://tampermonkey.net/ // @version 2.000000.0 // @description 搜索/检索单个网页并在原网页和弹窗显示多个搜索结果,支持动态搜索和显示方式选择,搜索不区分大小写,支持移动搜索容器和弹窗。 // @author 半竹 // @match *://*/* // @grant none // ==/UserScript== (function () { 'use strict'; // 不使用本地存储的位置,直接使用默认位置 const defaultPosition = { top: '10px', left: '10px' }; // 创建搜索容器 const searchContainer = document.createElement('div'); searchContainer.id = 'web-search-container'; searchContainer.style.position = 'fixed'; searchContainer.style.top = defaultPosition.top; searchContainer.style.left = defaultPosition.left; searchContainer.style.zIndex = 9999; searchContainer.style.backgroundColor = '#2c3e50'; searchContainer.style.padding = '10px'; searchContainer.style.borderRadius = '8px'; searchContainer.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)'; searchContainer.style.display = 'flex'; searchContainer.style.flexWrap = 'wrap'; searchContainer.style.alignItems = 'center'; searchContainer.style.gap = '8px'; searchContainer.style.transition = 'all 0.3s ease'; makeDraggable(searchContainer); // 创建显示/隐藏按钮 const toggleVisibilityButton = document.createElement('button'); toggleVisibilityButton.textContent = '隐藏'; toggleVisibilityButton.style.padding = '4px 8px'; toggleVisibilityButton.style.backgroundColor = '#95a5a6'; toggleVisibilityButton.style.color = 'white'; toggleVisibilityButton.style.border = 'none'; toggleVisibilityButton.style.borderRadius = '4px'; toggleVisibilityButton.style.cursor = 'pointer'; toggleVisibilityButton.style.fontSize = '10px'; toggleVisibilityButton.style.transition = 'background-color 0.3s ease'; toggleVisibilityButton.addEventListener('click', function () { const isVisible = searchContainer.style.display !== 'none'; if (isVisible) { searchContainer.style.display = 'none'; this.textContent = '显示搜索'; this.style.position = 'fixed'; this.style.top = defaultPosition.top; this.style.left = defaultPosition.left; this.style.zIndex = 10000; document.body.appendChild(this); } else { searchContainer.style.display = 'flex'; this.textContent = '隐藏'; this.style.position = 'static'; searchContainer.appendChild(this); } }); toggleVisibilityButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#7f8c8d'; }); toggleVisibilityButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#95a5a6'; }); // 创建搜索输入框 const searchInput = document.createElement('input'); searchInput.type = 'text'; searchInput.placeholder = '输入关键词,用1个分号或1~10个空格分隔'; searchInput.style.padding = '8px'; searchInput.style.border = 'none'; searchInput.style.borderRadius = '6px'; searchInput.style.fontSize = '12px'; searchInput.style.flexGrow = 1; searchInput.style.outline = 'none'; // 创建添加关键词按钮 const addKeywordButton = document.createElement('button'); addKeywordButton.textContent = '添加关键词'; addKeywordButton.style.padding = '8px 16px'; addKeywordButton.style.backgroundColor = '#3498db'; addKeywordButton.style.color = 'white'; addKeywordButton.style.border = 'none'; addKeywordButton.style.borderRadius = '6px'; addKeywordButton.style.cursor = 'pointer'; addKeywordButton.style.fontSize = '12px'; addKeywordButton.style.transition = 'background-color 0.3s ease'; addKeywordButton.addEventListener('click', function () { const newKeyword = prompt('请输入要添加的关键词:'); if (newKeyword) { const currentValue = searchInput.value; searchInput.value = currentValue? currentValue + ';' + newKeyword : newKeyword; } searchInput.focus(); }); addKeywordButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#2980b9'; }); addKeywordButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#3498db'; }); // 创建搜索动态数据按钮 const searchDynamicButton = document.createElement('button'); searchDynamicButton.textContent = '开启动态搜索'; searchDynamicButton.style.padding = '8px 16px'; searchDynamicButton.style.backgroundColor = '#f1c40f'; searchDynamicButton.style.color = 'white'; searchDynamicButton.style.border = 'none'; searchDynamicButton.style.borderRadius = '6px'; searchDynamicButton.style.cursor = 'pointer'; searchDynamicButton.style.fontSize = '12px'; searchDynamicButton.style.transition = 'background-color 0.3s ease'; searchDynamicButton.addEventListener('click', function () { isDynamicSearch =!isDynamicSearch; this.textContent = isDynamicSearch? '关闭动态搜索' : '开启动态搜索'; }); searchDynamicButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#f39c12'; }); searchDynamicButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#f1c40f'; }); // 创建搜索按钮 const searchButton = document.createElement('button'); searchButton.textContent = '搜索'; searchButton.style.padding = '8px 16px'; searchButton.style.backgroundColor = '#e74c3c'; searchButton.style.color = 'white'; searchButton.style.border = 'none'; searchButton.style.borderRadius = '6px'; searchButton.style.cursor = 'pointer'; searchButton.style.fontSize = '12px'; searchButton.style.transition = 'background-color 0.3s ease'; searchButton.addEventListener('click', function () { // 每次搜索都是全新的,清除之前的高亮和结果 clearHighlightedSpans(); clearPopupResultsContainer(); // 根据搜索范围收集文本节点 collectTextNodesByScope(); const inputKeywords = searchInput.value.trim(); if (inputKeywords) { // 支持分号或1~10个空格分隔关键词 let keywords = inputKeywords.split(';').map(part => part.trim()).filter(part => part!== ''); // 对每个部分再按空格分割 keywords = keywords.flatMap(part => part.split(/\s{1,10}/)).filter(keyword => keyword!== ''); const results = []; const useRegex = regexCheckbox.checked; textNodes.forEach(textNode => { const text = textNode.textContent; keywords.forEach(keyword => { let regex; try { if (useRegex) { regex = new RegExp(keyword, 'gi'); } else { const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); regex = new RegExp(escapedKeyword, 'gi'); } let match; while ((match = regex.exec(text))!== null) { results.push({ match: { node: textNode, index: match.index }, keyword: keyword }); } } catch (error) { console.error('正则表达式错误: ', error); alert('正则表达式错误: ' + error.message); return; } }); }); // 更新所有搜索结果 allSearchResults = results; const displayOption = displayOptionSelect.value; if (results.length > 0) { if (displayOption === 'popup') { showPopupResults(results); } else if (displayOption === 'original') { showOriginalResults(results); } else if (displayOption === 'combined') { showOriginalResults(results); showPopupResults(results); } } // 生成详细的搜索结果统计 const stats = generateSearchStats(results); searchResultStats.innerHTML = `找到 ${results.length} 个匹配结果
${stats}`; // 将本次搜索添加到历史记录 const searchEntry = { keywords: keywords, displayOption: displayOption }; searchHistory.push(searchEntry); localStorage.setItem('searchHistory', JSON.stringify(searchHistory)); displaySearchHistory(); } }); searchButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#c0392b'; }); searchButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#e74c3c'; }); // 显示方式选择 const displayOptionSelect = document.createElement('select'); const originalOption = document.createElement('option'); originalOption.value = 'original'; originalOption.textContent = '在原网页显示'; const popupOption = document.createElement('option'); popupOption.value = 'popup'; popupOption.textContent = '弹窗显示'; const combinedOption = document.createElement('option'); combinedOption.value = 'combined'; combinedOption.textContent = '合并显示'; displayOptionSelect.appendChild(originalOption); displayOptionSelect.appendChild(popupOption); displayOptionSelect.appendChild(combinedOption); displayOptionSelect.style.padding = '8px'; displayOptionSelect.style.border = 'none'; displayOptionSelect.style.borderRadius = '6px'; displayOptionSelect.style.fontSize = '12px'; displayOptionSelect.style.outline = 'none'; // 正则表达式搜索选项 const regexCheckbox = document.createElement('input'); regexCheckbox.type = 'checkbox'; regexCheckbox.id = 'regex-checkbox'; regexCheckbox.style.marginRight = '4px'; const regexLabel = document.createElement('label'); regexLabel.textContent = '正则表达式'; regexLabel.style.color = 'white'; regexLabel.style.fontSize = '12px'; regexLabel.style.cursor = 'pointer'; regexLabel.htmlFor = 'regex-checkbox'; const regexContainer = document.createElement('div'); regexContainer.style.display = 'flex'; regexContainer.style.alignItems = 'center'; regexContainer.appendChild(regexCheckbox); regexContainer.appendChild(regexLabel); // 高亮颜色选择器 const highlightColorLabel = document.createElement('label'); highlightColorLabel.textContent = '高亮颜色: '; highlightColorLabel.style.color = 'white'; highlightColorLabel.style.fontSize = '12px'; highlightColorLabel.style.marginRight = '4px'; const highlightColorInput = document.createElement('input'); highlightColorInput.type = 'color'; highlightColorInput.value = '#FFFF00'; // 默认黄色 highlightColorInput.style.width = '40px'; highlightColorInput.style.height = '24px'; highlightColorInput.style.border = 'none'; highlightColorInput.style.borderRadius = '4px'; highlightColorInput.style.cursor = 'pointer'; const colorContainer = document.createElement('div'); colorContainer.style.display = 'flex'; colorContainer.style.alignItems = 'center'; colorContainer.appendChild(highlightColorLabel); colorContainer.appendChild(highlightColorInput); // 搜索结果过滤输入框 const filterInput = document.createElement('input'); filterInput.type = 'text'; filterInput.placeholder = '过滤搜索结果'; filterInput.style.padding = '8px'; filterInput.style.border = 'none'; filterInput.style.borderRadius = '6px'; filterInput.style.fontSize = '12px'; filterInput.style.width = '100%'; filterInput.style.outline = 'none'; filterInput.addEventListener('input', function () { filterSearchResults(); }); // 创建搜索历史记录容器 const searchHistoryContainer = document.createElement('div'); searchHistoryContainer.style.width = '100%'; searchHistoryContainer.style.maxHeight = '120px'; searchHistoryContainer.style.overflowY = 'auto'; searchHistoryContainer.style.padding = '8px'; searchHistoryContainer.style.backgroundColor = '#34495e'; searchHistoryContainer.style.borderRadius = '6px'; searchHistoryContainer.style.marginTop = '8px'; searchHistoryContainer.style.color = 'white'; // 创建清除历史记录按钮 const clearHistoryButton = document.createElement('button'); clearHistoryButton.textContent = '清除历史记录'; clearHistoryButton.style.padding = '8px 16px'; clearHistoryButton.style.backgroundColor = '#95a5a6'; clearHistoryButton.style.color = 'white'; clearHistoryButton.style.border = 'none'; clearHistoryButton.style.borderRadius = '6px'; clearHistoryButton.style.cursor = 'pointer'; clearHistoryButton.style.fontSize = '12px'; clearHistoryButton.style.transition = 'background-color 0.3s ease'; clearHistoryButton.addEventListener('click', function () { searchHistory = []; localStorage.setItem('searchHistory', JSON.stringify(searchHistory)); displaySearchHistory(); }); clearHistoryButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#7f8c8d'; }); clearHistoryButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#95a5a6'; }); // 创建搜索结果统计容器 const searchResultStats = document.createElement('div'); searchResultStats.style.width = '100%'; searchResultStats.style.padding = '8px'; searchResultStats.style.backgroundColor = '#34495e'; searchResultStats.style.borderRadius = '6px'; searchResultStats.style.marginTop = '8px'; searchResultStats.style.color = 'white'; searchResultStats.style.fontSize = '12px'; // 历史记录筛选输入框 const historyFilterInput = document.createElement('input'); historyFilterInput.type = 'text'; historyFilterInput.placeholder = '筛选历史记录'; historyFilterInput.style.padding = '8px'; historyFilterInput.style.border = 'none'; historyFilterInput.style.borderRadius = '6px'; historyFilterInput.style.fontSize = '12px'; historyFilterInput.style.width = '100%'; historyFilterInput.style.outline = 'none'; historyFilterInput.addEventListener('input', function () { displaySearchHistory(); }); // 复制搜索结果按钮 const copyResultsButton = document.createElement('button'); copyResultsButton.textContent = '复制搜索结果'; copyResultsButton.style.padding = '8px 16px'; copyResultsButton.style.backgroundColor = '#1abc9c'; copyResultsButton.style.color = 'white'; copyResultsButton.style.border = 'none'; copyResultsButton.style.borderRadius = '6px'; copyResultsButton.style.cursor = 'pointer'; copyResultsButton.style.fontSize = '12px'; copyResultsButton.style.transition = 'background-color 0.3s ease'; copyResultsButton.addEventListener('click', function () { if (allSearchResults.length > 0) { let resultText = '搜索结果:\n\n'; allSearchResults.forEach((result, index) => { resultText += `${index + 1}. 关键词: ${result.keyword}\n`; resultText += ` 标签名: ${result.match.node.parentElement.tagName}\n`; resultText += ` 匹配位置: ${result.match.index}\n`; resultText += ` 内容: ${result.match.node.textContent.substring(0, 100)}${result.match.node.textContent.length > 100 ? '...' : ''}\n\n`; }); navigator.clipboard.writeText(resultText).then(() => { alert('搜索结果已复制到剪贴板'); }).catch(err => { console.error('复制失败: ', err); }); } else { alert('没有搜索结果可复制'); } }); copyResultsButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#16a085'; }); copyResultsButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#1abc9c'; }); // 导出搜索结果按钮 const exportResultsButton = document.createElement('button'); exportResultsButton.textContent = '导出搜索结果'; exportResultsButton.style.padding = '8px 16px'; exportResultsButton.style.backgroundColor = '#3498db'; exportResultsButton.style.color = 'white'; exportResultsButton.style.border = 'none'; exportResultsButton.style.borderRadius = '6px'; exportResultsButton.style.cursor = 'pointer'; exportResultsButton.style.fontSize = '12px'; exportResultsButton.style.transition = 'background-color 0.3s ease'; exportResultsButton.addEventListener('click', function () { if (allSearchResults.length > 0) { let resultText = '搜索结果\n\n'; allSearchResults.forEach((result, index) => { resultText += `${index + 1}. 关键词: ${result.keyword}\n`; resultText += ` 标签名: ${result.match.node.parentElement.tagName}\n`; resultText += ` 匹配位置: ${result.match.index}\n`; resultText += ` 内容: ${result.match.node.textContent.substring(0, 200)}${result.match.node.textContent.length > 200 ? '...' : ''}\n\n`; }); // 创建Blob对象 const blob = new Blob([resultText], { type: 'text/plain' }); const url = URL.createObjectURL(blob); // 创建下载链接 const a = document.createElement('a'); a.href = url; a.download = `搜索结果_${new Date().toISOString().slice(0, 10)}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); // 释放URL对象 URL.revokeObjectURL(url); alert('搜索结果已导出为文件'); } else { alert('没有搜索结果可导出'); } }); exportResultsButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#2980b9'; }); exportResultsButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#3498db'; }); // 搜索结果排序选择器 const sortSelect = document.createElement('select'); sortSelect.style.padding = '8px'; sortSelect.style.border = 'none'; sortSelect.style.borderRadius = '6px'; sortSelect.style.fontSize = '12px'; sortSelect.style.outline = 'none'; const sortOptions = [ { value: 'default', text: '默认顺序' }, { value: 'keyword', text: '按关键词排序' }, { value: 'position', text: '按匹配位置排序' }, { value: 'tag', text: '按标签名排序' } ]; sortOptions.forEach(option => { const opt = document.createElement('option'); opt.value = option.value; opt.textContent = option.text; sortSelect.appendChild(opt); }); sortSelect.addEventListener('change', function () { sortSearchResults(); saveSearchConfig(); }); // 搜索范围选择器 const scopeSelect = document.createElement('select'); scopeSelect.style.padding = '8px'; scopeSelect.style.border = 'none'; scopeSelect.style.borderRadius = '6px'; scopeSelect.style.fontSize = '12px'; scopeSelect.style.outline = 'none'; const scopeOptions = [ { value: 'entire', text: '整个页面' }, { value: 'visible', text: '可见区域' }, { value: 'selected', text: '选中元素' } ]; scopeOptions.forEach(option => { const opt = document.createElement('option'); opt.value = option.value; opt.textContent = option.text; scopeSelect.appendChild(opt); }); // 为其他配置控件添加事件监听器,自动保存配置 displayOptionSelect.addEventListener('change', saveSearchConfig); regexCheckbox.addEventListener('change', saveSearchConfig); highlightColorInput.addEventListener('change', saveSearchConfig); scopeSelect.addEventListener('change', saveSearchConfig); // 打乱搜索结果顺序按钮 const shuffleResultsButton = document.createElement('button'); shuffleResultsButton.textContent = '打乱结果顺序'; shuffleResultsButton.style.padding = '8px 16px'; shuffleResultsButton.style.backgroundColor = '#9b59b6'; shuffleResultsButton.style.color = 'white'; shuffleResultsButton.style.border = 'none'; shuffleResultsButton.style.borderRadius = '6px'; shuffleResultsButton.style.cursor = 'pointer'; shuffleResultsButton.style.fontSize = '12px'; shuffleResultsButton.style.transition = 'background-color 0.3s ease'; shuffleResultsButton.addEventListener('click', function () { if (allSearchResults.length > 0) { // 清除之前的高亮和结果 clearHighlightedSpans(); // 打乱结果顺序 allSearchResults = shuffleArray(allSearchResults); // 根据当前显示选项重新显示结果 const displayOption = displayOptionSelect.value; if (displayOption === 'popup') { showPopupResults(allSearchResults); } else if (displayOption === 'original') { showOriginalResults(allSearchResults); } else if (displayOption === 'combined') { showOriginalResults(allSearchResults); showPopupResults(allSearchResults); } } }); shuffleResultsButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#8e44ad'; }); shuffleResultsButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#9b59b6'; }); // 创建切换样式按钮 const toggleStyleButton = document.createElement('button'); toggleStyleButton.textContent = '切换样式'; toggleStyleButton.style.padding = '8px 16px'; toggleStyleButton.style.backgroundColor = '#d35400'; toggleStyleButton.style.color = 'white'; toggleStyleButton.style.border = 'none'; toggleStyleButton.style.borderRadius = '6px'; toggleStyleButton.style.cursor = 'pointer'; toggleStyleButton.style.fontSize = '12px'; toggleStyleButton.style.transition = 'background-color 0.3s ease'; toggleStyleButton.addEventListener('click', function () { if (isFullScreen) { // 恢复默认样式 searchContainer.style.top = defaultPosition.top; searchContainer.style.left = defaultPosition.left; searchContainer.style.width = 'auto'; searchContainer.style.height = 'auto'; searchContainer.style.padding = '10px'; searchContainer.style.flexDirection = 'row'; searchContainer.style.flexWrap = 'wrap'; searchContainer.style.alignItems = 'center'; searchContainer.style.gap = '8px'; } else { // 切换到卡片样式 searchContainer.style.top = '10%'; searchContainer.style.left = '10%'; searchContainer.style.width = '300px'; searchContainer.style.height = 'auto'; searchContainer.style.padding = '20px'; searchContainer.style.flexDirection = 'column'; searchContainer.style.flexWrap = 'nowrap'; searchContainer.style.alignItems = 'flex-start'; searchContainer.style.gap = '4px'; } isFullScreen =!isFullScreen; }); toggleStyleButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#e67e22'; }); toggleStyleButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#d35400'; }); searchContainer.appendChild(searchInput); searchContainer.appendChild(addKeywordButton); searchContainer.appendChild(searchDynamicButton); searchContainer.appendChild(searchButton); searchContainer.appendChild(displayOptionSelect); searchContainer.appendChild(regexContainer); searchContainer.appendChild(colorContainer); searchContainer.appendChild(sortSelect); searchContainer.appendChild(scopeSelect); searchContainer.appendChild(filterInput); searchContainer.appendChild(historyFilterInput); searchContainer.appendChild(searchHistoryContainer); searchContainer.appendChild(clearHistoryButton); searchContainer.appendChild(searchResultStats); searchContainer.appendChild(copyResultsButton); searchContainer.appendChild(exportResultsButton); searchContainer.appendChild(shuffleResultsButton); searchContainer.appendChild(toggleStyleButton); searchContainer.appendChild(toggleVisibilityButton); // 检查搜索容器是否已经存在,若不存在则添加到页面 const existingContainer = document.getElementById('web-search-container'); if (!existingContainer) { document.body.appendChild(searchContainer); } // 全局变量 let textNodes = []; // 用于存储最新的文本节点 let currentlyHighlighted = null; // 记录当前高亮的元素 let searchHistory = JSON.parse(localStorage.getItem('searchHistory')) || []; // 搜索历史记录数组 let currentPage = 1; // 当前页码 const resultsPerPage = 10; // 每页显示的结果数量 let allSearchResults = []; // 存储所有搜索结果 let isDynamicSearch = false; // 是否开启动态搜索 let isFullScreen = false; // 是否切换到卡片样式 // 显示搜索历史记录 function displaySearchHistory() { searchHistoryContainer.innerHTML = ''; const filter = historyFilterInput.value.toLowerCase(); searchHistory.forEach((entry, index) => { const keywordsStr = entry.keywords.join(', '); const displayStr = entry.displayOption; const historyItemText = `${keywordsStr} (${displayStr})`; if (historyItemText.toLowerCase().includes(filter)) { const historyItem = document.createElement('div'); historyItem.textContent = historyItemText; historyItem.style.padding = '6px'; historyItem.style.borderBottom = '1px solid #4e6379'; historyItem.style.cursor = 'pointer'; historyItem.addEventListener('click', () => { searchInput.value = entry.keywords.join(';'); displayOptionSelect.value = entry.displayOption; performSearch(); }); searchHistoryContainer.appendChild(historyItem); } }); } // 收集文本节点的函数 function collectTextNodes(node) { if (node.nodeType === Node.TEXT_NODE) { textNodes.push(node); } else { const children = node.childNodes; for (let i = 0; i < children.length; i++) { collectTextNodes(children[i]); } } } // 根据搜索范围收集文本节点 function collectTextNodesByScope() { textNodes = []; const scope = scopeSelect.value; switch (scope) { case 'visible': // 只收集可见区域的文本节点 const visibleElements = document.querySelectorAll('*'); visibleElements.forEach(element => { const rect = element.getBoundingClientRect(); if (rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0) { collectTextNodes(element); } }); break; case 'selected': // 只收集选中元素的文本节点 const selection = window.getSelection(); if (selection && selection.rangeCount > 0) { const range = selection.getRangeAt(0); collectTextNodes(range.commonAncestorContainer); } else { // 如果没有选中元素,收集整个页面 collectTextNodes(document.body); } break; default: // 收集整个页面的文本节点 collectTextNodes(document.body); break; } } // 在原网页显示结果 function showOriginalResults(results) { // 按文本节点分组,确保每个节点只处理一次 const nodeMap = new Map(); results.forEach((result, index) => { const node = result.match.node; if (!nodeMap.has(node)) { nodeMap.set(node, []); } nodeMap.get(node).push({ result, index }); }); // 处理每个文本节点 nodeMap.forEach((matches, node) => { const text = node.textContent; let newText = text; // 按匹配位置从后向前处理,避免替换后位置偏移 matches.sort((a, b) => b.result.match.index - a.result.match.index); matches.forEach(({ result, index }) => { const keyword = result.keyword; const start = result.match.index; const end = start + keyword.length; // 替换匹配部分 const before = newText.substring(0, start); const matched = newText.substring(start, end); const after = newText.substring(end); newText = before + `${matched}` + after; }); // 替换节点内容 const range = document.createRange(); range.selectNodeContents(node); const fragment = range.createContextualFragment(newText); node.parentNode.replaceChild(fragment, node); }); } // 显示弹窗结果 function showPopupResults(results) { const popup = window.open('', '搜索结果', 'width=400,height=400'); const popupBody = popup.document.body; popupBody.style.padding = '10px'; results.forEach((result, index) => { const p = document.createElement('p'); const node = result.match.node; const text = node.textContent; const originalKeyword = result.keyword; const keyword = originalKeyword; const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp(escapedKeyword, 'gi'); const newText = text.replace(regex, `${originalKeyword}`); p.innerHTML = `${index + 1}. ${newText}`; p.addEventListener('click', function () { result.match.node.parentElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); // 标黄色高亮 const spans = result.match.node.parentElement.querySelectorAll('span'); spans.forEach(span => { if (span.textContent === originalKeyword) { span.style.backgroundColor = 'yellow'; } }); }); p.addEventListener('mouseover', function () { p.style.backgroundColor = '#f9f9f9'; }); p.addEventListener('mouseout', function () { p.style.backgroundColor = 'white'; }); popupBody.appendChild(p); }); } // 清除原网页中高亮显示的 标签 function clearHighlightedSpans() { const highlightedSpans = document.querySelectorAll('span[style*="background-color"]'); highlightedSpans.forEach(span => { const parent = span.parentNode; const textNode = document.createTextNode(span.textContent); parent.insertBefore(textNode, span); parent.removeChild(span); }); } // 清除弹窗中的搜索结果容器 function clearPopupResultsContainer() { // 由于弹窗是通过window.open创建的新窗口,这里我们不需要特殊处理 // 每次搜索都会创建新的弹窗,旧弹窗会自动关闭或保持打开状态 } // 获取唯一的颜色 function getUniqueColor(index) { const baseColor = highlightColorInput.value; // 如果用户选择了自定义颜色,则使用该颜色 if (baseColor) { return baseColor; } // 否则使用默认颜色数组 const baseColors = ['#FFFF00', '#FF00FF', '#00FFFF', '#FFA500', '#00FF00']; return baseColors[index % baseColors.length]; } // 打乱数组顺序 function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } // 使元素可拖动 function makeDraggable(element) { let isDragging = false; let offsetX, offsetY; element.addEventListener('mousedown', function (e) { isDragging = true; offsetX = e.clientX - parseInt(element.style.left); offsetY = e.clientY - parseInt(element.style.top); }); document.addEventListener('mousemove', function (e) { if (isDragging) { element.style.left = (e.clientX - offsetX) + 'px'; element.style.top = (e.clientY - offsetY) + 'px'; } }); document.addEventListener('mouseup', function () { isDragging = false; }); } // 初始化显示历史记录 displaySearchHistory(); // 加载搜索配置 loadSearchConfig(); // 初始化收集文本节点 collectTextNodesByScope(); // 过滤搜索结果 function filterSearchResults() { const filterText = filterInput.value.toLowerCase().trim(); if (filterText) { const filteredResults = allSearchResults.filter(result => { return result.keyword.toLowerCase().includes(filterText) || result.match.node.textContent.toLowerCase().includes(filterText); }); // 清除之前的高亮 clearHighlightedSpans(); // 根据当前显示选项重新显示过滤后的结果 const displayOption = displayOptionSelect.value; if (filteredResults.length > 0) { if (displayOption === 'popup') { showPopupResults(filteredResults); } else if (displayOption === 'original') { showOriginalResults(filteredResults); } else if (displayOption === 'combined') { showOriginalResults(filteredResults); showPopupResults(filteredResults); } } // 生成详细的搜索结果统计 const stats = generateSearchStats(filteredResults); searchResultStats.innerHTML = `找到 ${filteredResults.length} 个匹配结果 (过滤后)
${stats}`; } else { // 清除之前的高亮 clearHighlightedSpans(); // 显示所有结果 const displayOption = displayOptionSelect.value; if (allSearchResults.length > 0) { if (displayOption === 'popup') { showPopupResults(allSearchResults); } else if (displayOption === 'original') { showOriginalResults(allSearchResults); } else if (displayOption === 'combined') { showOriginalResults(allSearchResults); showPopupResults(allSearchResults); } } // 生成详细的搜索结果统计 const stats = generateSearchStats(allSearchResults); searchResultStats.innerHTML = `找到 ${allSearchResults.length} 个匹配结果
${stats}`; } } // 排序搜索结果 function sortSearchResults() { const sortBy = sortSelect.value; let sortedResults = [...allSearchResults]; switch (sortBy) { case 'keyword': sortedResults.sort((a, b) => a.keyword.localeCompare(b.keyword)); break; case 'position': sortedResults.sort((a, b) => a.match.index - b.match.index); break; case 'tag': sortedResults.sort((a, b) => a.match.node.parentElement.tagName.localeCompare(b.match.node.parentElement.tagName)); break; default: // 默认顺序,不排序 break; } // 清除之前的高亮 clearHighlightedSpans(); // 根据当前显示选项重新显示排序后的结果 const displayOption = displayOptionSelect.value; if (sortedResults.length > 0) { if (displayOption === 'popup') { showPopupResults(sortedResults); } else if (displayOption === 'original') { showOriginalResults(sortedResults); } else if (displayOption === 'combined') { showOriginalResults(sortedResults); showPopupResults(sortedResults); } } } // 生成搜索结果统计信息 function generateSearchStats(results) { if (results.length === 0) { return '无匹配结果'; } // 统计每个关键词的匹配次数 const keywordStats = {}; // 统计每个标签的匹配次数 const tagStats = {}; results.forEach(result => { // 统计关键词 if (!keywordStats[result.keyword]) { keywordStats[result.keyword] = 0; } keywordStats[result.keyword]++; // 统计标签 const tag = result.match.node.parentElement.tagName; if (!tagStats[tag]) { tagStats[tag] = 0; } tagStats[tag]++; }); // 生成统计信息 let stats = ''; // 关键词统计 stats += '关键词统计:
'; Object.entries(keywordStats).forEach(([keyword, count]) => { stats += `• ${keyword}: ${count} 次
`; }); // 标签统计 stats += '标签统计:
'; Object.entries(tagStats).forEach(([tag, count]) => { stats += `• ${tag}: ${count} 次
`; }); return stats; } // 保存搜索配置 function saveSearchConfig() { const config = { displayOption: displayOptionSelect.value, useRegex: regexCheckbox.checked, highlightColor: highlightColorInput.value, sortBy: sortSelect.value, searchScope: scopeSelect.value }; localStorage.setItem('searchConfig', JSON.stringify(config)); } // 加载搜索配置 function loadSearchConfig() { const config = JSON.parse(localStorage.getItem('searchConfig')); if (config) { if (config.displayOption) { displayOptionSelect.value = config.displayOption; } if (config.useRegex !== undefined) { regexCheckbox.checked = config.useRegex; } if (config.highlightColor) { highlightColorInput.value = config.highlightColor; } if (config.sortBy) { sortSelect.value = config.sortBy; } if (config.searchScope) { scopeSelect.value = config.searchScope; } } } // 执行搜索函数 function performSearch() { // 清除之前的高亮和结果 clearHighlightedSpans(); clearPopupResultsContainer(); // 执行搜索 searchButton.click(); } // 键盘快捷键支持 document.addEventListener('keydown', function (e) { // Ctrl+F - 聚焦搜索输入框 if (e.ctrlKey && e.key === 'f') { e.preventDefault(); searchInput.focus(); } // Enter - 执行搜索(当搜索输入框聚焦时) if (e.key === 'Enter' && document.activeElement === searchInput) { e.preventDefault(); performSearch(); } // Esc - 清除搜索输入 if (e.key === 'Escape' && document.activeElement === searchInput) { searchInput.value = ''; } // Ctrl+Shift+C - 复制搜索结果 if (e.ctrlKey && e.shiftKey && e.key === 'C') { e.preventDefault(); copyResultsButton.click(); } // Ctrl+Shift+E - 导出搜索结果 if (e.ctrlKey && e.shiftKey && e.key === 'E') { e.preventDefault(); exportResultsButton.click(); } }); })();