Greasy Fork is available in English.
全维度处理链接/表单/动态内容,支持Web Components和现代框架
当前为
// ==UserScript==
// @name 当页开链
// @namespace http://greasyfork.icu/zh-CN/scripts/497533
// @version 2.5
// @description 全维度处理链接/表单/动态内容,支持Web Components和现代框架
// @author meteora & YourName
// @match *://*/*
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @run-at document-body
// ==/UserScript==
(function() {
'use strict';
// 协议安全名单(2025标准)
const SAFE_PROTOCOLS = new Set([
'http:', 'https:', 'ftp:',
'mailto:', 'tel:', 'sms:'
]);
// Shadow DOM处理器(支持5级嵌套)
const processShadowRoot = (root, depth = 0) => {
if (depth > 5) return;
root.querySelectorAll('a, form').forEach(processElement);
root.querySelectorAll('*').forEach(node => {
if (node.shadowRoot) {
processShadowRoot(node.shadowRoot, depth + 1);
}
});
};
// 增强元素处理逻辑
const processElement = (element) => {
// 协议过滤
try {
const href = element.href?.toLowerCase() || '';
const protocol = new URL(href, location.href).protocol;
if (!SAFE_PROTOCOLS.has(protocol)) return;
} catch(e) {
return;
}
// 多维度属性清理
['target', 'onclick', 'data-target'].forEach(attr => {
element.removeAttribute(attr);
});
// 表单提交劫持
if (element.matches('form') && !element.__patched) {
element.__patched = true;
const originalSubmit = element.submit;
element.submit = function() {
this.target = '_self';
originalSubmit.call(this);
};
}
};
// 全局基础策略
const applyBaseStrategy = () => {
if (!document.querySelector('base')) {
const base = document.createElement('base');
base.target = '_self';
document.head.prepend(base);
}
};
// 智能观察器(性能优化版)
const initObserver = () => {
const observer = new MutationObserver(mutations => {
mutations.forEach(({ addedNodes, attributeName }) => {
// 处理新增节点
addedNodes.forEach(node => {
if (node.nodeType === 1) {
processElement(node);
processShadowRoot(node);
}
});
// 处理属性变更
if (attributeName === 'target') {
mutations.forEach(({ target }) => processElement(target));
}
});
});
observer.observe(document, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['target', 'href']
});
};
// 路由劫持层(支持现代框架)
const hijackRouting = () => {
const routerApis = [
history.pushState,
history.replaceState,
unsafeWindow.router?.navigate
];
routerApis.forEach(fn => {
const original = fn;
fn = (...args) => {
const result = original.apply(this, args);
setTimeout(() => {
document.querySelectorAll('a').forEach(processElement);
}, requestAnimationFrame(() => 0));
return result;
};
});
};
// 核心初始化
const main = () => {
if (window.self !== window.top) return;
applyBaseStrategy();
hijackRouting();
// 初始处理
document.querySelectorAll('a, form').forEach(processElement);
processShadowRoot(document);
// 动态处理
initObserver();
// 全局事件拦截
document.addEventListener('click', e => {
const target = e.composedPath()[0];
if (target.matches('a')) processElement(target);
}, { capture: true, passive: true });
// 强化window.open处理
unsafeWindow.open = function(url, target, ...args) {
return window.open(url, '_self', ...args);
};
};
// 启动逻辑
if (document.readyState === 'complete') {
main();
} else {
document.addEventListener('DOMContentLoaded', main);
}
// 调试功能
GM_registerMenuCommand('强制刷新处理', () => {
document.querySelectorAll('a, form').forEach(processElement);
processShadowRoot(document);
});
})();