// ==UserScript== // @name BZ综合网页搜索脚本 // @namespace http://tampermonkey.net/ // @version 1.0.11 // @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'; makeDraggable(searchContainer); // 创建搜索输入框 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'; let isDynamicSearch = false; 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(); const inputKeywords = searchInput.value.trim(); if (inputKeywords) { const keywords = inputKeywords.split(';').map(keyword => keyword.trim()).filter(keyword => keyword!== ''); const results = []; textNodes.forEach(textNode => { const text = textNode.textContent; keywords.forEach(keyword => { const index = text.indexOf(keyword); if (index!== -1) { results.push({ match: { node: textNode }, keyword: keyword }); } }); }); 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 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 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 () { let resultText = ''; allSearchResults.forEach(result => { resultText += `关键词: ${result.keyword}, 标签名: ${result.match.node.parentElement.tagName}, 内容: ${result.match.node.textContent}\n`; }); navigator.clipboard.writeText(resultText).then(() => { alert('搜索结果已复制到剪贴板'); }).catch(err => { console.error('复制失败: ', err); }); }); copyResultsButton.addEventListener('mouseover', function () { this.style.backgroundColor = '#16a085'; }); copyResultsButton.addEventListener('mouseout', function () { this.style.backgroundColor = '#1abc9c'; }); // 打乱搜索结果顺序按钮 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 () { allSearchResults = shuffleArray(allSearchResults); performSearch(); }); 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'; let isFullScreen = false; 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(historyFilterInput); searchContainer.appendChild(searchHistoryContainer); searchContainer.appendChild(clearHistoryButton); searchContainer.appendChild(searchResultStats); searchContainer.appendChild(copyResultsButton); searchContainer.appendChild(shuffleResultsButton); searchContainer.appendChild(toggleStyleButton); // 检查搜索容器是否已经存在,若不存在则添加到页面 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 = []; // 显示搜索历史记录 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 showOriginalResults(results) { results.forEach((result, index) => { 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}`); 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() { const existingResultsContainer = document.querySelector('div[style*="position: fixed; top: 60px; right: 10px; z-index: 9999;"]'); if (existingResultsContainer) { document.body.removeChild(existingResultsContainer); } } // 获取唯一的颜色 function getUniqueColor(index) { const baseColors = ['#FFFF00', '#FF00FF', '#00FFFF', '#FFA500', '#00FF00']; const hueShift = (index % baseColors.length) * 360 / baseColors.length; const saturation = '80%'; const lightness = '50%'; return `hsl(${hueShift}, ${saturation}, ${lightness})`; } // 打乱数组顺序 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(); // 初始化收集文本节点 collectTextNodes(document.body); // 执行搜索函数 function performSearch() { searchButton.click(); } })();