Force Links in Current Tab

Forces all links (a tags) and window.open() to open in the current tab (_self).

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         Force Links in Current Tab
// @name:zh-CN   强制链接在当前标签页打开
// @namespace    http://tampermonkey.net/
// @version      2025.11.01
// @description  Forces all links (a tags) and window.open() to open in the current tab (_self).
// @description:zh-CN  强制所有链接 (a 标签) 和 JavaScript 的 window.open() 都在当前标签页中打开,并处理鼠标中键点击。
// @author       Your Name
// @match        *://*/*
// @grant        none
// @license MIT 
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // --- 1. 修补 (Patch) window.open ---
    // 必须在 @run-at document-start 模式下运行,以确保在页面上任何脚本之前执行
    const originalWindowOpen = window.open;

    window.open = function(url, target, features, replace) {
        // 打印日志以便调试 (可选)
        // console.log(`[Force Current Tab] window.open called: url=${url}, target=${target}`);

        // 强制 target 为 _self
        // 将参数转换为数组,以便安全修改
        const args = [...arguments];

        if (args.length < 2) {
            // 如果只有 url,默认是新窗口,我们添加 _self
            args[1] = '_self';
        } else {
            // 如果有 target,我们强制覆盖为 _self
            args[1] = '_self';
        }

        // 使用 .apply 来调用原始函数,保持正确的 'this' 上下文和修改后的参数
        return originalWindowOpen.apply(this, args);
    };

    // --- 2. 处理页面上现有的和动态添加的 <a> 标签 ---

    /**
     * 辅助函数:处理单个节点或节点树中的所有 <a> 标签
     * @param {Node} node - 要处理的根节点 (例如 document.body 或新添加的元素)
     */
    const processLinks = (node) => {
        // 确保我们有 querySelectorAll 方法 (即它是一个元素)
        if (!node || typeof node.querySelectorAll !== 'function') {
            return;
        }

        // 查找所有 <a> 标签
        const links = node.querySelectorAll('a');
        links.forEach(link => {
            if (link.target && link.target !== '_self') {
                // console.log(`[Force Current Tab] Fixing link: ${link.href}`);
                link.target = '_self';

                // 移除 rel=noopener 和 rel=noreferrer,因为它们主要用于新标签页
                // link.removeAttribute('rel');
                // 注意:移除 'rel' 可能会破坏某些网站样式或功能,所以保持修改 target 即可
            }
        });
    };

    // --- 3. 页面加载时执行一次 ---
    document.addEventListener('DOMContentLoaded', () => {
        processLinks(document.body);
    });

    // --- 4. 使用 MutationObserver 监视动态添加的 <a> 标签 ---
    const observer = new MutationObserver((mutationsList) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(newNode => {
                    // 检查新添加的节点本身是否是 <a>
                    if (newNode.tagName === 'A' && newNode.target && newNode.target !== '_self') {
                        newNode.target = '_self';
                    }
                    // 检查新添加的节点是否 *包含* <a> 标签
                    if (newNode.nodeType === Node.ELEMENT_NODE) {
                        processLinks(newNode);
                    }
                });
            }
        }
    });

    // 等待 body 出现后再开始观察
    // 因为脚本在 document-start 运行,body 可能还不存在
    if (document.body) {
        observer.observe(document.body, { childList: true, subtree: true });
    } else {
        document.addEventListener('DOMContentLoaded', () => {
            observer.observe(document.body, { childList: true, subtree: true });
        });
    }

    // --- 5. 拦截鼠标中键点击 (auxclick) ---
    // 这将阻止中键在新标签页中打开链接
    document.addEventListener('auxclick', (e) => {
        if (e.button === 1) { // 1 = 鼠标中键
            const link = e.target.closest('a[href]');
            if (link) {
                // 阻止默认的中键点击行为(在新标签页打开)
                e.preventDefault();
                e.stopPropagation();
                e.stopImmediatePropagation();

                // 在当前标签页导航
                window.location.href = link.href;
            }
        }
    }, true); // 使用捕获阶段 (true) 来确保在任何其他脚本之前运行

    // --- 6. 拦截普通点击 (作为最后的保险) ---
    // 尽管 MutationObserver 应该已经处理了,
    // 但这个捕获阶段的点击监听器可以捕获到任何“漏网之鱼”。
    document.addEventListener('click', (e) => {
        const link = e.target.closest('a[href]');
        if (link && link.target && link.target !== '_self') {
            // 确保 target 在点击时也被设置为 _self
            link.target = '_self';
        }
    }, true); // 同样使用捕获阶段

})();