浏览器文本补全脚本

发布时间:2026/10/7 11:22:04

浏览器文本补全脚本 一、功能展示如何在网页里面实现文本补全呢在油猴里面粘贴并保存下面的完整代码然后在网页文本框里面输入索引词就会弹出待选项按下按钮即可自动替换。补全功能如图所示。二、添加地方在下面代码区域添加你想要补全的内容。例打印: console.log();冒号前面为索引词冒号后面为待选项使用中括号就是有多个待选项。const SNIPPETS { 打印: console.log();, 报错: console.error();, log: [ console.log();, console.log(DEBUG:, ...);, console.log(JSON.stringify(obj, null, 2));, console.table(data); ], 循环: [ for (let i 0; i arr.length; i) {\n const item arr[i];\n \n}, arr.forEach((item, index) {\n \n});, for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n \n }\n} ], 函数: [ function name() {\n \n}, const name () {\n \n};, async function name() {\n try {\n \n } catch (err) {\n console.error(err);\n }\n} ], 判断: if (condition) {\n \n}, 试试: try {\n \n} catch (err) {\n console.error(err);\n}, 框架: !DOCTYPE html\nhtml\nhead\n meta charsetUTF-8\n title文档/title\n/head\nbody\n \n/body\n/html, 请求: axios.get(/api).then(res {\n console.log(res.data);\n}); };三、完整代码// UserScript // name 智能代码补全自动定位 可拖拽 // namespace http://tampermonkey.net/ // version 4.0 // description 下拉菜单自动防遮挡支持鼠标拖拽移动兼容React/Vue // author You // match *://*/* // grant none // /UserScript (function() { use strict; // // ★★★ 配置区域 ★★★ // const SNIPPETS { 打印: console.log();, 报错: console.error();, log: [ console.log();, console.log(DEBUG:, ...);, console.log(JSON.stringify(obj, null, 2));, console.table(data); ], 循环: [ for (let i 0; i arr.length; i) {\n const item arr[i];\n \n}, arr.forEach((item, index) {\n \n});, for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n \n }\n} ], 函数: [ function name() {\n \n}, const name () {\n \n};, async function name() {\n try {\n \n } catch (err) {\n console.error(err);\n }\n} ], 判断: if (condition) {\n \n}, 试试: try {\n \n} catch (err) {\n console.error(err);\n}, 框架: !DOCTYPE html\nhtml\nhead\n meta charsetUTF-8\n title文档/title\n/head\nbody\n \n/body\n/html, 请求: axios.get(/api).then(res {\n console.log(res.data);\n}); }; // let menuDiv null; let isComposing false; let activeIndex -1; let currentMatches []; let currentWord ; let currentInput null; let currentPage 0; const PAGE_SIZE 9; // ---- 拖拽相关 ---- let isDragging false; let dragOffsetX 0; let dragOffsetY 0; let dragHandle null; // ---- 创建菜单 ---- function createMenu() { if (menuDiv) return; menuDiv document.createElement(div); menuDiv.style.cssText position: fixed; background: #ffffff; border: 1px solid #d1d5db; border-radius: 8px; box-shadow: 0 6px 16px rgba(0,0,0,0.15); max-height: 300px; width: 320px; overflow-y: auto; font-family: Segoe UI, Consolas, monospace; z-index: 999999; display: none; padding: 0 0 4px 0; user-select: none; ; document.body.appendChild(menuDiv); // 创建拖拽手柄顶部灰色条 dragHandle document.createElement(div); dragHandle.style.cssText height: 12px; background: #f3f4f6; cursor: grab; border-radius: 8px 8px 0 0; display: flex; align-items: center; justify-content: center; font-size: 10px; color: #9ca3af; letter-spacing: 4px; border-bottom: 1px solid #e5e7eb; ; dragHandle.textContent ⋮⋮⋮; // 拖拽点阵 dragHandle.addEventListener(mousedown, startDrag); menuDiv.appendChild(dragHandle); } // ---- 拖拽逻辑 ---- function startDrag(e) { e.preventDefault(); e.stopPropagation(); isDragging true; const rect menuDiv.getBoundingClientRect(); dragOffsetX e.clientX - rect.left; dragOffsetY e.clientY - rect.top; menuDiv.style.cursor grabbing; document.addEventListener(mousemove, onDrag); document.addEventListener(mouseup, endDrag); } function onDrag(e) { if (!isDragging) return; e.preventDefault(); let left e.clientX - dragOffsetX; let top e.clientY - dragOffsetY; // 防止拖出屏幕边界 left Math.max(0, Math.min(left, window.innerWidth - menuDiv.offsetWidth)); top Math.max(0, Math.min(top, window.innerHeight - menuDiv.offsetHeight)); menuDiv.style.left left px; menuDiv.style.top top px; menuDiv.style.right auto; menuDiv.style.bottom auto; } function endDrag(e) { isDragging false; menuDiv.style.cursor default; document.removeEventListener(mousemove, onDrag); document.removeEventListener(mouseup, endDrag); } // ---- 智能定位 ---- function positionMenu() { if (!menuDiv || !currentInput) return; const rect currentInput.getBoundingClientRect(); const menuWidth menuDiv.scrollWidth || 320; const menuHeight menuDiv.scrollHeight || 300; // 先计算初始位置默认在输入框下方 let left rect.left; let top rect.bottom 4; // 1. 水平方向修正 if (left menuWidth window.innerWidth) { left window.innerWidth - menuWidth - 10; } if (left 0) left 10; // 2. 垂直方向修正优先在下方下方不够就跳到上方 if (top menuHeight window.innerHeight) { // 检查上方空间是否足够 if (rect.top menuHeight 10) { top rect.top - menuHeight - 4; } else { // 上下都不够就紧贴屏幕底部 top window.innerHeight - menuHeight - 10; } } if (top 0) top 10; menuDiv.style.left left px; menuDiv.style.top top px; menuDiv.style.right auto; menuDiv.style.bottom auto; } // ---- 渲染页面 ---- function renderPage() { if (!menuDiv) return; const start currentPage * PAGE_SIZE; const end start PAGE_SIZE; const pageItems currentMatches.slice(start, end); const totalPages Math.ceil(currentMatches.length / PAGE_SIZE); // 保留拖拽手柄清除其余内容 while (menuDiv.childNodes.length 0) { if (menuDiv.childNodes[0] dragHandle) break; menuDiv.removeChild(menuDiv.childNodes[0]); } // 确保手柄在第一位 if (menuDiv.firstChild ! dragHandle) { menuDiv.insertBefore(dragHandle, menuDiv.firstChild); } // 删除手柄之后的所有内容 while (menuDiv.childNodes.length 1) { menuDiv.removeChild(menuDiv.childNodes[1]); } if (pageItems.length 0 currentPage 0) { currentPage 0; renderPage(); return; } // 1. 渲染代码选项 pageItems.forEach((item, idx) { const num idx 1; const div document.createElement(div); div.style.cssText padding: 6px 12px; cursor: pointer; color: #333; border-bottom: 1px solid #f3f4f6; font-size: 12px; line-height: 1.5; display: flex; align-items: flex-start; white-space: pre-wrap; word-break: break-all; user-select: none; ; const numSpan document.createElement(span); numSpan.style.cssText display: inline-block; width: 24px; color: #2563eb; font-weight: bold; margin-right: 8px; flex-shrink: 0; ; numSpan.textContent num; const codeSpan document.createElement(span); codeSpan.textContent item.snippet; div.appendChild(numSpan); div.appendChild(codeSpan); div.addEventListener(mousedown, (e) { e.preventDefault(); e.stopPropagation(); }); div.addEventListener(click, (e) { e.stopPropagation(); const globalIdx currentPage * PAGE_SIZE idx; applySnippet(globalIdx); hideMenu(); }); div.addEventListener(mouseenter, () { activeIndex idx; highlightItem(activeIndex); }); menuDiv.appendChild(div); }); // 2. 底部翻页栏 if (totalPages 1) { const footer document.createElement(div); footer.style.cssText display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; border-top: 1px solid #e5e7eb; background: #f9fafb; font-size: 12px; color: #6b7280; position: sticky; bottom: 0; user-select: none; ; const prev document.createElement(span); prev.textContent ‹ 上一页; prev.style.cssText cursor: pointer; padding: 2px 6px; border-radius: 4px;; prev.addEventListener(click, (e) { e.stopPropagation(); if (currentPage 0) { currentPage--; renderPage(); } }); if (currentPage 0) prev.style.opacity 0.5; const info document.createElement(span); info.textContent ${currentPage 1} / ${totalPages}; const next document.createElement(span); next.textContent 下一页 ›; next.style.cssText cursor: pointer; padding: 2px 6px; border-radius: 4px;; next.addEventListener(click, (e) { e.stopPropagation(); if (currentPage totalPages - 1) { currentPage; renderPage(); } }); if (currentPage totalPages - 1) next.style.opacity 0.5; footer.appendChild(prev); footer.appendChild(info); footer.appendChild(next); menuDiv.appendChild(footer); } // 先显示再定位防止获取高度为0 menuDiv.style.display block; // 使用 requestAnimationFrame 确保DOM渲染完成后再计算位置 requestAnimationFrame(() { positionMenu(); }); activeIndex -1; } function highlightItem(index) { // 跳过第一个手柄只高亮代码项 const items menuDiv.querySelectorAll(div:not(:first-child):not(:last-child)); items.forEach((el, i) { el.style.background i index ? #e3f2fd : transparent; }); } function showMenu(inputEl, word, matches) { if (!menuDiv) createMenu(); currentInput inputEl; currentWord word; currentMatches matches; currentPage 0; if (!matches || matches.length 0) { hideMenu(); return; } renderPage(); } function hideMenu() { if (menuDiv) menuDiv.style.display none; currentMatches []; activeIndex -1; currentPage 0; // 如果拖拽状态未结束强制清除 if (isDragging) endDrag(); } // ---- 核心插入逻辑保留 ---- function applySnippet(globalIdx) { if (!currentInput || !currentWord) return; if (globalIdx 0 || globalIdx currentMatches.length) return; const replacement currentMatches[globalIdx].snippet; if (!replacement) return; const isTextarea currentInput.tagName TEXTAREA || currentInput.tagName INPUT; const isEditable currentInput.isContentEditable; if (isTextarea) { const start currentInput.selectionStart; const text currentInput.value; const before text.substring(0, start); const regex /([a-zA-Z0-9_\u4e00-\u9fa5])$/; const match before.match(regex); if (match match[1] currentWord) { const newText text.substring(0, start - currentWord.length) replacement text.substring(start); const newPos start - currentWord.length replacement.length; let valueSetter null; let proto Object.getPrototypeOf(currentInput); while (proto !valueSetter) { const desc Object.getOwnPropertyDescriptor(proto, value); if (desc desc.set) valueSetter desc.set; proto Object.getPrototypeOf(proto); } if (valueSetter) { valueSetter.call(currentInput, newText); } else { currentInput.value newText; } currentInput.selectionStart currentInput.selectionEnd newPos; currentInput.dispatchEvent(new Event(input, { bubbles: true })); currentInput.dispatchEvent(new Event(change, { bubbles: true })); setTimeout(() { if (!currentInput) return; if (currentInput.value ! newText) { if (valueSetter) { valueSetter.call(currentInput, newText); } else { currentInput.value newText; } currentInput.selectionStart currentInput.selectionEnd newPos; currentInput.dispatchEvent(new Event(input, { bubbles: true })); } }, 0); } } else if (isEditable) { const sel window.getSelection(); if (!sel.rangeCount) return; const range sel.getRangeAt(0); const node range.startContainer; if (node.nodeType Node.TEXT_NODE) { const text node.textContent; const offset range.startOffset; const before text.substring(0, offset); const regex /([a-zA-Z0-9_\u4e00-\u9fa5])$/; const match before.match(regex); if (match match[1] currentWord) { range.setStart(node, offset - currentWord.length); range.setEnd(node, offset); sel.removeAllRanges(); sel.addRange(range); document.execCommand(insertText, false, replacement); } } } } // ---- 事件监听保持原有逻辑 ---- document.addEventListener(input, function(e) { if (isComposing) return; const el document.activeElement; if (!el) return; const isInput el.tagName TEXTAREA || el.tagName INPUT || el.isContentEditable; if (!isInput) return; let text, start; if (el.tagName TEXTAREA || el.tagName INPUT) { text el.value; start el.selectionStart; } else { const sel window.getSelection(); if (!sel.rangeCount) return; const range sel.getRangeAt(0); const node range.startContainer; if (node.nodeType ! Node.TEXT_NODE) return; text node.textContent; start range.startOffset; } const before text.substring(0, start); const regex /([a-zA-Z0-9_\u4e00-\u9fa5])$/; const match before.match(regex); if (match) { const word match[1]; if (word.length 1) { hideMenu(); return; } const expanded []; Object.keys(SNIPPETS).forEach(key { if (key.includes(word)) { const val SNIPPETS[key]; if (Array.isArray(val)) { val.forEach(snippet expanded.push({ key, snippet })); } else { expanded.push({ key, snippet: val }); } } }); if (expanded.length 0) { showMenu(el, word, expanded); } else { hideMenu(); } } else { hideMenu(); } }); document.addEventListener(keydown, function(e) { if (!menuDiv || menuDiv.style.display none) return; if (!isComposing e.key 1 e.key 9) { e.preventDefault(); const num parseInt(e.key) - 1; const globalIdx currentPage * PAGE_SIZE num; if (globalIdx currentMatches.length) { applySnippet(globalIdx); hideMenu(); } return; } if (e.key PageDown || e.key ArrowRight) { e.preventDefault(); const total Math.ceil(currentMatches.length / PAGE_SIZE); if (currentPage total - 1) { currentPage; renderPage(); } return; } if (e.key PageUp || e.key ArrowLeft) { e.preventDefault(); if (currentPage 0) { currentPage--; renderPage(); } return; } if (e.key ArrowDown) { e.preventDefault(); const pageItems currentMatches.slice(currentPage * PAGE_SIZE, (currentPage 1) * PAGE_SIZE); if (pageItems.length 0) return; activeIndex (activeIndex 1) % pageItems.length; highlightItem(activeIndex); } else if (e.key ArrowUp) { e.preventDefault(); const pageItems currentMatches.slice(currentPage * PAGE_SIZE, (currentPage 1) * PAGE_SIZE); if (pageItems.length 0) return; activeIndex (activeIndex - 1 pageItems.length) % pageItems.length; highlightItem(activeIndex); } else if (e.key Enter) { e.preventDefault(); if (activeIndex 0) { const pageItems currentMatches.slice(currentPage * PAGE_SIZE, (currentPage 1) * PAGE_SIZE); if (activeIndex pageItems.length) { const globalIdx currentPage * PAGE_SIZE activeIndex; applySnippet(globalIdx); hideMenu(); } } } else if (e.key Escape) { hideMenu(); } }); document.addEventListener(mousedown, function(e) { if (menuDiv !menuDiv.contains(e.target)) { hideMenu(); } }); document.addEventListener(compositionstart, function() { isComposing true; }); document.addEventListener(compositionend, function() { isComposing false; const el document.activeElement; if (el) el.dispatchEvent(new Event(input, { bubbles: true })); }); })();
延伸阅读

更多相关文章

2026/10/6 11:47:09

零基础具身智能机械臂实战项目开发全套视频课程百度云网盘下载

在具身智能的宏大叙事中,机械臂不再仅仅是执行预设轨迹的自动化设备,而是进化为具备感知、决策与交互能力的智能体。要实现这一跨越,核心在于打破单一传感器的局限,将视觉、触觉、力觉等多模态数据在底层进行深度融合。这种融合并…

2026/10/6 11:47:23

微信生态远程控制工具QClaw技术解析与应用

1. 项目概述:QClaw的核心功能解析腾讯QClaw是一款基于微信生态的远程控制工具,其核心创新点在于实现了微信联系人列表与远程控制功能的深度整合。简单来说,它允许用户直接通过微信聊天窗口对目标设备发起远程控制,无需额外安装客户…

2026/10/6 11:51:02

VMware虚拟机安装配置全攻略:从硬件兼容到性能优化

1. 为什么虚拟机安装总在第一步就卡住?如果你正准备在电脑上搭建一个开发环境、测试一个新系统,或者只是想安全地运行一些来源不明的软件,那么“在VMware里装个虚拟机”这个念头,大概率是你最先想到的方案。听起来简单直接&#x…

2026/10/7 11:21:12

eFuse与8位MCU协同:工业电源路径保护与PMBus监控实现

嵌入式系统里做电源的人,大多都经历过这种场景:整机联调到一半,现场传来消息说某一路供电打挂了,要么保险丝烧断,要么DC-DC芯片直接冒烟。排查到最后,往往就是插拔瞬间的浪涌、负载侧的意外短路&#xff0c…

2026/10/7 11:21:12

工业多接口线缆设计实战:物理层重构与可靠性工程

1. 项目概述:一根线缆如何扛起工业现场的“神经中枢”任务“Multi-Connector Cable Assembly for Industrial Hardware”——这名字听起来像设备手册里一句冷冰冰的技术参数,但在我干了十二年工业连接系统设计与交付的实操经验里,它其实是产线…

2026/10/7 11:21:12

Linux下Intel AX210网卡5GHz热点速率提升与hostapd配置指南

刚拿到一块 Intel AX210 网卡,满心欢喜想在 Linux 下给手机开个热点,结果 5GHz 频段压根找不到,即使勉强连上,速度也只有可怜的几十兆,跟标称的千兆级速率差了十万八千里。如果你也遇到过类似情况,大概率不…

2026/10/7 11:21:12

Java+JavaScript+HTML水质检测系统:毕设课设完整源码部署详解

简介:面向高校计算机及相关专业的毕业设计、课程设计与项目开发场景,这套基于Java、JavaScript与HTML技术栈实现的水质检测系统提供完整可运行的项目源码与配套数据库,解决水质数据采集、检测结果展示及相关管理流程的Web端实现问题。系统涵盖…

2026/10/7 11:21:12

WordPress营销数据跟踪全指南:从埋点到GA4事件追踪

做WordPress网站这些年,最常被问到的一句话是:“我花钱投了广告,访客来了不少,为什么转化还是上不去?”每次遇到这个问题,我都会先让对方打开后台看一眼营销数据——大部分时候,他们的网站连最基…

2026/10/7 11:16:10

Agent-Reach 实战:用 CLI 为 AI Agent 构建执行层

1. Agent-Reach 到底是个什么东西第一次看到 Agent-Reach 这个名字,我下意识以为是某个新出的 AI 浏览器插件,或者又是一个套壳的聊天客户端。直到我把它的 CLI 跑起来,才意识到这东西的定位其实很明确:它是一个让 AI Agent 能够&…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/7 8:18:33

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

ESP32免重刷固件:浏览器直接修改NVS键值实现WiFi配置更新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

SAP HANA查询结果导出CSV:避开乱码、性能与权限的实用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

数字后端Placement阶段Density与Congestion控制实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑