// ==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();
}
})();