您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
搜索引擎导航增强,支持拖拽、缩放、折叠和状态记忆,加载更稳定。
// ==UserScript== // @name 搜索引擎增强 // @namespace search_enhance_namespace // @version 4.0.0 // @description 搜索引擎导航增强,支持拖拽、缩放、折叠和状态记忆,加载更稳定。 // @author zyh // @match *://www.baidu.com/* // @match *://www.so.com/s* // @match *://www.sogou.com/web* // @match *://cn.bing.com/search* // @match *://www.bing.com/search* // @match *://www.google.com/search* // @match *://www.google.com.hk/search* // @grant GM_getValue // @grant GM_setValue // @license MIT // ==/UserScript== (function() { 'use strict'; /** * SearchEnhancer 类,用于封装所有功能 */ class SearchEnhancer { /** * 构造函数,脚本的入口 */ constructor() { this.host = window.location.host; this.initData(); // 检查当前网站是否是目标搜索引擎 this.engineConfig = this.searchEnginesData.find(engine => this.host.includes(engine.host)); if (!this.engineConfig) { return; // 如果不是,则不执行任何操作 } // 使用 waitForElement 等待搜索框加载完成后,再初始化UI // 这是为了确保我们操作的DOM元素已经存在 this.waitForElement(this.engineConfig.elementInput, () => { this.settings = this.loadSettings(); // 确保折叠前的高度有一个合理的默认值 this.lastExpandedHeight = this.settings.height > 40 ? this.settings.height : 400; this.initUI(); }); } /** * 初始化搜索引擎和导航链接的数据 */ initData() { this.searchEnginesData = [ {host: "baidu.com", name: "百度", elementInput: "#kw"}, {host: "so.com", name: "360搜索", elementInput: "#keyword"}, {host: "sogou.com", name: "搜狗", elementInput: "#upquery"}, {host: "bing.com", name: "必应", elementInput: "#sb_form_q"}, {host: "google.com", name: "谷歌", elementInput: "input[name='q'],textarea[name='q']"} ]; this.navigationData = [ {"name": "搜索引擎", "list": [ {"name": "百度", "url": "https://www.baidu.com/s?wd=@@"}, {"name": "必应", "url": "https://cn.bing.com/search?q=@@"}, {"name": "Google", "url": "https://www.google.com/search?q=@@"} ]}, {"name": "综合搜索", "list": [ {"name": "知乎", "url": "https://www.zhihu.com/search?q=@@"}, {"name": "CSDN", "url": "https://so.csdn.net/so/search?q=@@"}, {"name": "GitHub", "url": "https://github.com/search?q=@@"}, {"name": "小红书", "url": "https://www.xiaohongshu.com/search_result?keyword=@@"} ]}, {"name": "视频搜索", "list": [ {"name": "B站", "url": "https://search.bilibili.com/all?keyword=@@"}, {"name": "抖音", "url": "https://www.douyin.com/search/@@"}, {"name": "YouTube", "url": "https://www.youtube.com/results?search_query=@@"} ]}, {"name": "学术搜索", "list": [ {"name": "谷粉学术", "url": "https://www.defineabc.com/scholar?hl=en&q=@@" }, {"name": "Aminer", "url": "https://www.aminer.cn/search?t=b&q=@@"} ]} ]; } /** * 初始化UI:创建面板、应用样式、绑定事件 */ initUI() { if (document.getElementById('search-enhancer-panel')) return; // 防止重复创建 this.createPanel(); this.applyStyles(); this.attachEventListeners(); } /** * 从油猴存储中加载设置 */ loadSettings() { const defaults = { x: 20, y: 120, width: 280, height: 400, isCollapsed: false }; const saved = GM_getValue(`enhancer_settings_${this.host}`) || {}; return { ...defaults, ...saved }; // 合并默认值和已保存值 } /** * 保存当前设置到油猴存储 */ saveSettings() { if (!this.panel) return; const currentSettings = { x: this.panel.offsetLeft, y: this.panel.offsetTop, width: this.panel.offsetWidth, height: this.lastExpandedHeight, // 关键:总是保存展开时的高度 isCollapsed: this.panel.classList.contains('collapsed') }; GM_setValue(`enhancer_settings_${this.host}`, currentSettings); } /** * 创建UI面板的HTML结构 */ createPanel() { let html = '<div class="nav-header"><div class="nav-title">搜索导航</div><button class="nav-toggle-btn">▲</button></div>'; html += '<div class="nav-content">'; this.navigationData.forEach(cat => { html += `<div class="nav-section"><div class="section-title">${cat.name}</div><div class="nav-links">`; cat.list.forEach(item => { html += `<a href="#" data-url="${item.url}">${item.name}</a>`; }); html += '</div></div>'; }); html += '</div><div class="resize-handle"></div>'; this.panel = document.createElement('div'); this.panel.id = 'search-enhancer-panel'; this.panel.innerHTML = html; document.body.appendChild(this.panel); // 如果保存的状态是折叠的,则初始化为折叠状态 if (this.settings.isCollapsed) { this.panel.classList.add('collapsed'); this.panel.querySelector('.nav-content').style.display = 'none'; this.panel.querySelector('.nav-toggle-btn').textContent = '▼'; } } /** * 应用CSS样式 */ applyStyles() { const s = this.settings; const css = ` #search-enhancer-panel { position:fixed; top:${s.y}px; left:${s.x}px; width:${s.width}px; height:${s.isCollapsed ? 'auto' : `${s.height}px`}; min-width:200px; min-height:40px; z-index:999999; display:flex; flex-direction:column; background:rgba(255,255,255,0.40); border-radius:10px; box-shadow:0 5px 20px rgba(0,0,0,0.12); backdrop-filter:blur(8px); border:1px solid rgba(0,0,0,0.08); user-select:none; overflow:hidden; transition: height 0.2s ease-in-out; } #search-enhancer-panel.no-transition { transition: none !important; } #search-enhancer-panel.collapsed { height: auto !important; } .nav-header { display:flex; justify-content:space-between; align-items:center; padding:8px 12px; background:rgba(0,0,0,0.04); cursor:move; flex-shrink: 0; } .nav-title { font-weight:600; color:#333; } .nav-toggle-btn { border:none; background:none; cursor:pointer; font-size:16px; color:#555; padding:5px; } .nav-content { padding:10px 15px; overflow-y:auto; flex-grow:1; } .nav-section { margin-bottom:12px; } .section-title { font-size:13px; font-weight:500; color:#666; margin-bottom:8px; padding-bottom:4px; border-bottom:1px solid #eee; } .nav-links { display:flex; flex-wrap:wrap; gap:8px; } .nav-links a { padding:4px 9px; color:#333; text-decoration:none; font-size:13px; background:#f1f1f1; border-radius:5px; transition:all 0.2s; } .nav-links a:hover { background:#007bff; color:white; transform:translateY(-1px); } .resize-handle { position:absolute; bottom:0; right:0; width:15px; height:15px; cursor:se-resize; z-index:10; } `; const styleEl = document.createElement('style'); styleEl.textContent = css; document.head.appendChild(styleEl); } /** * 绑定所有事件监听器 */ attachEventListeners() { const header = this.panel.querySelector('.nav-header'); const toggleBtn = this.panel.querySelector('.nav-toggle-btn'); const resizeHandle = this.panel.querySelector('.resize-handle'); // 折叠/展开 toggleBtn.addEventListener('click', e => { e.stopPropagation(); const isCollapsed = this.panel.classList.toggle('collapsed'); this.panel.querySelector('.nav-content').style.display = isCollapsed ? 'none' : 'block'; toggleBtn.textContent = isCollapsed ? '▼' : '▲'; if (!isCollapsed) { this.panel.style.height = `${this.lastExpandedHeight}px`; } this.saveSettings(); }); // 链接点击 this.panel.querySelectorAll('.nav-links a').forEach(link => { link.addEventListener('click', e => { e.preventDefault(); const keywordInput = document.querySelector(this.engineConfig.elementInput); const keyword = keywordInput ? keywordInput.value : ''; const url = e.target.dataset.url.replace('@@', encodeURIComponent(keyword)); window.open(url, '_blank'); }); }); // 拖拽和缩放 const dragOrResize = (e, type) => { e.preventDefault(); this.panel.classList.add('no-transition'); // 拖拽时禁用过渡动画,防止卡顿 let startX = e.clientX, startY = e.clientY; let initialX = this.panel.offsetLeft, initialY = this.panel.offsetTop; let initialW = this.panel.offsetWidth, initialH = this.panel.offsetHeight; let animationFrameId = null; const onMove = (moveEvent) => { if (animationFrameId) { cancelAnimationFrame(animationFrameId); } animationFrameId = requestAnimationFrame(() => { let dx = moveEvent.clientX - startX, dy = moveEvent.clientY - startY; if (type === 'drag') { let newX = Math.max(0, Math.min(window.innerWidth - this.panel.offsetWidth, initialX + dx)); let newY = Math.max(0, Math.min(window.innerHeight - this.panel.offsetHeight, initialY + dy)); this.panel.style.left = `${newX}px`; this.panel.style.top = `${newY}px`; } else { // resize let newW = Math.max(200, initialW + dx); let newH = Math.max(100, initialH + dy); this.panel.style.width = `${newW}px`; if (!this.panel.classList.contains('collapsed')) { this.panel.style.height = `${newH}px`; } } }); }; const onEnd = () => { if (animationFrameId) { cancelAnimationFrame(animationFrameId); } if (!this.panel.classList.contains('collapsed')) { this.lastExpandedHeight = this.panel.offsetHeight; } this.saveSettings(); document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onEnd); this.panel.classList.remove('no-transition'); // 拖拽结束后恢复过渡动画 }; document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onEnd); }; header.addEventListener('mousedown', e => { if (e.target === header || e.target.classList.contains('nav-title')) dragOrResize(e, 'drag'); }); resizeHandle.addEventListener('mousedown', e => dragOrResize(e, 'resize')); } /** * 等待指定元素出现在DOM中 * @param {string} selector - CSS选择器 * @param {function} callback - 元素出现后执行的回调函数 */ waitForElement(selector, callback) { const interval = setInterval(() => { if (document.querySelector(selector)) { clearInterval(interval); callback(); } }, 200); } } // 确保在DOM加载完成后再执行脚本,这是最关键的一步 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => new SearchEnhancer()); } else { // 如果DOM已经加载完成,则直接执行 new SearchEnhancer(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址