您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
高性能文本转链接方案,支持动态内容
// ==UserScript== // @name 网页文本转链接 // @description 高性能文本转链接方案,支持动态内容 // @version 1.0 // @author WJ // @match *://*/* // @license MIT // @grant none // @run-at document-idle // @namespace https://greasyfork.org/users/914996 // ==/UserScript== (() => { // 1. URL 正则 const tlds = [ 'app','aero','aer','art','asia','beer','biz','cat','cc','chat','ci','cloud', 'club','cn','com','cool','coop','co','dev','edu','email','fit','fun','gov', 'group','hk','host','icu','info','ink','int','io','jobs','kim','love','ltd', 'luxe','me','mil','mobi','moe','museum','name','net','nl','network','one', 'online','org','plus','post','press','pro','red','ren','run','ru','shop', 'site','si','space','store','tech','tel','top','travel','tv','tw','uk','us', 'video','vip','wang','website','wiki','wml','work','ws','xin','xyz','yoga','zone' ].join('|'); const urlRegex = new RegExp( String.raw`(?:(?:https?:\/\/)|(?:www\.|wap\.))[\w.:/?=%&#@+~-]{1,50}\.[\w]{2,15}\b[\w.:/?=%&#@+~-]*|`+ String.raw`(?<!(https?:|@)\S*)\b[\w:.-]{1,50}\.(?:${tlds})\b[\w.:/?=%&#@+~-]*`, 'gi' ); // 2. 检查节点是否需处理 const guard = root => root && !root.closest?.(` :is(a,applet,area,button,canvas,code,cite,embed,frame,frameset,head, iframe,img,input,map,meta,noscript,object,option,pre,script,select,style,svg,textarea), [contenteditable],.WJ_modal,.ace_editor,.CodeMirror,.monaco-editor,.cm-editor`); // 3. 处理节点 const guardok = root => { const walker = document.createTreeWalker(root, 4, { acceptNode: n => guard(n.parentElement) ? 1 : 2 }); const tasks = []; for (let node; (node = walker.nextNode());) { const raw = node.textContent ?? ''; const replaced = raw.replace(urlRegex, m => `<a style="text-decoration:underline" href="${m.startsWith('http')?m:'https://'+m}">${m}</a>`); raw !== replaced && tasks.push({ node, replaced }); } tasks.forEach(({ node, replaced }) => node.replaceWith(document.createRange().createContextualFragment(replaced)) ); }; // 4. 初始化 setTimeout(() => { const io = new IntersectionObserver(en => en.forEach(({ isIntersecting, target }) => isIntersecting && (io.unobserve(target), requestIdleCallback?.(() => guardok(target), { timeout: 1000 })) )); const mo = new MutationObserver(mu => mu.forEach(({ addedNodes }) => addedNodes.forEach(no => no.nodeType === 1 && guard(no) && io.observe(no)) )).observe(document.body, { childList: true, subtree: true }); [...document.body.children].forEach(el => guard(el) && io.observe(el)); }, 1000); })();