ThunderStore: Force Links to Open in New Tab

强制 ThunderStore 所有链接在新标签页打开

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         ThunderStore: Force Links to Open in New Tab
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  强制 ThunderStore 所有链接在新标签页打开
// @match        https://thunderstore.io/c/hollow-knight-silksong/
// @license MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function forceLinksNewTab(root=document) {
        const links = root.querySelectorAll('a[href]');
        for (const a of links) {
            a.setAttribute('target', '_blank');
            a.setAttribute('rel', 'noopener noreferrer'); // 安全最佳实践
        }
    }

    // 初始化处理页面已有链接
    forceLinksNewTab();

    // 监听后续 DOM 变化,处理动态生成的链接
    const observer = new MutationObserver(mutations => {
        for (const m of mutations) {
            for (const node of m.addedNodes) {
                if (node.nodeType === 1) { // element
                    if (node.matches?.('a[href]')) {
                        node.setAttribute('target', '_blank');
                        node.setAttribute('rel', 'noopener noreferrer');
                    }
                    // 如果新增的是一个容器,里面可能有多个 a
                    forceLinksNewTab(node);
                }
            }
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();