OPGG界面优化与交互优化

OPGG的英雄分析页面和游戏模式页面的元素排版优化、英雄分析页面点击英雄名字链接跳转方式改为'新建标签页打开'

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         OPGG界面优化与交互优化
// @name:en      OPGG UI optimization and interaction improvements
// @name:en-GB   OPGG UI optimization and interaction improvements
// @name:ko      OP.GG의 UI 개선 및 인터랙션 최적화
// @name:zh      OPGG界面优化与交互优化
// @name:zh-CN   OPGG界面优化与交互优化
// @name:zh-HK   OPGG 介面優化與互動優化
// @name:zh-MO   OPGG 介面優化與互動優化
// @name:zh-MY   OPGG界面优化与交互优化
// @name:zh-SG   OPGG界面优化与交互优化
// @name:zh-TW   OPGG 介面優化與互動優化
// @namespace    http://tampermonkey.net/
// @version      2.0.1
// @description  OPGG的英雄分析页面和游戏模式页面的元素排版优化、英雄分析页面点击英雄名字链接跳转方式改为'新建标签页打开'
// @description:en     Optimize the layout of elements on OPGG’s Champions page and Game modes page, and change the champion name links on the Champions page so they open in a new tab.
// @description:en-GB  Optimize the layout of elements on OPGG’s Champions page and Game modes page, and change the champion name links on the Champions page so they open in a new tab.
// @description:ko     OPGG의 챔피언 분석 페이지와 게임 모드 페이지의 요소 배치를 최적화하고, 챔피언 분석 페이지에서 챔피언 이름을 클릭할 때 링크가‘새 탭에서 열리도록’수정합니다.
// @description:zh     OPGG的英雄分析页面和游戏模式页面的元素排版优化、英雄分析页面点击英雄名字链接跳转方式改为'新建标签页打开'
// @description:zh-CN  OPGG的英雄分析页面和游戏模式页面的元素排版优化、英雄分析页面点击英雄名字链接跳转方式改为'新建标签页打开'
// @description:zh-HK  優化 OPGG 的英雄分析頁面與遊戲模式頁面的元素排版,並將英雄分析頁面中點擊英雄名稱的連結改為『在新分頁中開啟』
// @description:zh-MO  優化 OPGG 的英雄分析頁面與遊戲模式頁面的元素排版,並將英雄分析頁面中點擊英雄名稱的連結改為『在新分頁中開啟』
// @description:zh-MY  OPGG的英雄分析页面和游戏模式页面的元素排版优化、英雄分析页面点击英雄名字链接跳转方式改为'新建标签页打开'
// @description:zh-SG  OPGG的英雄分析页面和游戏模式页面的元素排版优化、英雄分析页面点击英雄名字链接跳转方式改为'新建标签页打开'
// @description:zh-TW  優化 OPGG 的英雄分析頁面與遊戲模式頁面的元素排版,並將英雄分析頁面中點擊英雄名稱的連結改為『在新分頁中開啟』
// @license      Copyright © 2025 Leon. All rights reserved.
// @author       Leon
// @match        https://op.gg/zh-cn* 
// @match        https://op.gg/ko*
// @match        https://op.gg/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=op.gg
// @run-at       document-start
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // ==========================================
    // 1. 静态常量定义 (避免GC压力)
    // ==========================================
    const STYLE_ID = 'my-optimized-spa-style';

    // 预定义CSS字符串,避免运行时拼接
    const CSS_CHAMPIONS = `
        @media (min-width: 1080px) {
            .md\\:w-width-limit { width: 1600px; }
            .md\\:text-lg { margin-left: 200px; }
            .md\\:w-\\[740px\\] { width: 830px; }
            .md\\:w-\\[332px\\] { width: 532px; }
        }
        .w-\\[332px\\] { width: 532px; }
        .flex-row-reverse { flex-direction: row; margin-left: 100px; }
        .flex { display: flex; justify-content: center; }
        .md\\:w-auto { justify-content: flex-start !important; }
        #content-header { display: none; }
    `;

    const CSS_MODES = `
        .md\\:max-w-\\[332px\\], .md\\:gap-2 { width: 334px !important; }
        .md\\:max-w-\\[332px\\] { max-width: 405px; }
        #content-header { display: none; }
    `;

    // ==========================================
    // 2. 状态缓存 (核心性能优化点)
    // ==========================================
    // 缓存 DOM 节点引用
    let _cachedStyleEl = null;
    // 缓存当前页面类型,防止重复渲染
    let _currentType = null;

    // 懒加载获取 Style 标签,终身只操作一次 DOM 插入
    function getStyleElement() {
        if (_cachedStyleEl) return _cachedStyleEl;
        _cachedStyleEl = document.getElementById(STYLE_ID);
        if (!_cachedStyleEl) {
            _cachedStyleEl = document.createElement('style');
            _cachedStyleEl.id = STYLE_ID;
            _cachedStyleEl.type = 'text/css';
            document.head.appendChild(_cachedStyleEl);
        }
        return _cachedStyleEl;
    }

    // ==========================================
    // 3. 样式应用逻辑
    // ==========================================
    function updateStyles() {
        const path = location.pathname;
        let newType = 'other';

        // 快速判断页面类型 (使用 indexOf 比 includes 微快,且兼容性更好)
        if (/\/champions\/?$/.test(path)) {
            newType = 'champions';
        } else if (path.indexOf('modes') !== -1) {
            newType = 'modes';
        }

        // 【性能关键】如果页面类型没变,直接退出!
        // 避免浏览器执行耗时的 Recalculate Style
        if (newType === _currentType) return;

        // 更新状态
        _currentType = newType;
        const styleEl = getStyleElement();

        // 只有状态改变时才写入 DOM
        if (newType === 'champions') {
            styleEl.textContent = CSS_CHAMPIONS;
        } else if (newType === 'modes') {
            styleEl.textContent = CSS_MODES;
        } else {
            styleEl.textContent = ''; // 清理样式
        }

    }

    // ==========================================
    // 4. 点击拦截逻辑 (Event Hot Path)
    // ==========================================
    function handleClick(e) {
        // 【性能关键】快速检查:利用缓存变量判断。
        // 如果当前不是 champions 页面,直接结束函数,不进行任何 DOM 遍历。
        if (_currentType !== 'champions') return;

        // 只有确定在 champions 页面,才开始查找 DOM
        // closest 是原生 C++ 实现,速度很快,但能省则省
        const link = e.target.closest('a');
        if (!link || !link.href) return;

        // 检查父级容器:精准匹配 class
        const targetContainer = link.closest('.flex.flex-row-reverse.gap-2');

        if (targetContainer) {
            link.target = "_blank";
            e.stopPropagation(); // 阻止 SPA 路由接管
        }
    }

    // ==========================================
    // 5. 初始化与事件绑定
    // ==========================================

    // 绑定点击事件 (捕获阶段)
    document.addEventListener('click', handleClick, true);

    // 劫持 History API
    const _historyWrap = function(type) {
        const orig = history[type];
        return function() {
            const rv = orig.apply(this, arguments);
            const e = new Event(type);
            e.arguments = arguments;
            window.dispatchEvent(e);
            return rv;
        };
    };
    history.pushState = _historyWrap('pushState');
    history.replaceState = _historyWrap('replaceState');

    // 绑定路由事件
    // 使用同一个处理函数,减少内存占用
    window.addEventListener('pushState', updateStyles);
    window.addEventListener('replaceState', updateStyles);
    window.addEventListener('popstate', updateStyles);

    // 首次执行
    updateStyles();

})();