自定义标签页标题

为任意URL永久设置一个自定义的标签页标题,并强制保持,即使网站动态修改标题。

当前为 2025-07-18 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         自定义标签页标题
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  为任意URL永久设置一个自定义的标签页标题,并强制保持,即使网站动态修改标题。
// @description:en Permanently set a custom tab title for any URL and force it to stay, even if the site dynamically changes the title.
// @author       yorhaha
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license MIT
// ==/UserScript==

(async function() {
    'use strict';

    // 关键改动:判断当前脚本是否在顶层窗口运行,如果是在iframe中则直接退出。
    // This is the key fix: check if we are in the top window. If in an iframe, exit.
    if (window.self !== window.top) {
        return;
    }

    const currentUrl = window.location.href;
    let customTitle = await GM_getValue(currentUrl);
    let observer = null;

    // 强制设置并保持标题的函数
    const forceTitle = (title) => {
        // 如果观察者已存在,先断开,避免在自己修改标题时触发死循环
        if (observer) {
            observer.disconnect();
        }

        document.title = title;

        // 创建或重新配置 MutationObserver 来监视标题变化
        const titleElement = document.querySelector('head > title');
        if (titleElement) {
            observer = new MutationObserver((mutations) => {
                // 如果检测到标题被外部脚本修改,则强制改回来
                if (document.title !== title) {
                    console.log(`[Custom Title] 网站尝试将标题修改为 "${document.title}",已恢复为 "${title}"`);
                    document.title = title;
                }
            });
            // 监视 <title> 元素的子节点(即文本内容)的变化
            observer.observe(titleElement, { childList: true });
        }
    };

    // 1. 页面加载时,检查并应用已保存的自定义标题
    if (customTitle) {
        console.log(`[Custom Title] 已加载自定义标题: "${customTitle}"`);
        // 使用 DOMContentLoaded 事件确保 <head> 和 <title> 元素已加载
        window.addEventListener('DOMContentLoaded', (event) => {
            forceTitle(customTitle);
        });
        // 对于某些单页应用,可能需要更早或更晚地执行,@run-at document-start 配合 DOMContentLoaded 是一个比较稳妥的方案
        forceTitle(customTitle); // 立即尝试设置一次
    }

    // 2. 注册菜单命令:设置/修改标题
    GM_registerMenuCommand('设置/修改当前页标题', async () => {
        const newTitle = prompt('请输入当前页面的新标题:', document.title);

        // 如果用户输入了内容并且没有取消
        if (newTitle !== null && newTitle.trim() !== '') {
            customTitle = newTitle.trim();
            await GM_setValue(currentUrl, customTitle);
            console.log(`[Custom Title] 已为当前URL设置新标题: "${customTitle}"`);
            forceTitle(customTitle); // 立即应用并开始监视
            alert(`标题已成功设置为: "${customTitle}"`);
        } else if (newTitle !== null) {
            alert('标题不能为空,操作已取消。');
        }
    });

    // 3. 注册菜单命令:恢复默认标题
    GM_registerMenuCommand('恢复当前页默认标题', async () => {
        if (await GM_getValue(currentUrl)) {
            if (confirm('确定要删除该页面的自定义标题并恢复默认吗?页面将会刷新。')) {
                await GM_deleteValue(currentUrl);
                if (observer) {
                    observer.disconnect(); // 停止监视
                }
                console.log('[Custom Title] 已删除当前URL的自定义标题,页面将刷新。');
                alert('自定义标题已删除,页面将重新加载以恢复原始标题。');
                window.location.reload();
            }
        } else {
            alert('当前页面没有设置自定义标题。');
        }
    });
})();