YouTube – Prevent Scrolling to Top When Clicking Timestamps

Prevents YouTube from scrolling to the top when clicking timestamps in comments. This makes it easier to use picture-in-picture mode while reading comments, without being pulled back to the top of the page.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name                YouTube – Prevent Scrolling to Top When Clicking Timestamps
// @name:zh-TW          YouTube - 點擊留言時間戳記時防止捲動到最上方
// @namespace           https://github.com/micr0dust
// @version             1.0.0
// @description         Prevents YouTube from scrolling to the top when clicking timestamps in comments. This makes it easier to use picture-in-picture mode while reading comments, without being pulled back to the top of the page.
// @description:zh-tw   點留言時間戳記時不讓頁面自動捲動到最上方,這樣就能開子母畫面看留言,又不用擔心點時間戳記會被拉到最上面。
// @match               https://www.youtube.com/*
// @grant               none
// @icon                https://www.google.com/s2/favicons?domain=youtube.com
// @license             MIT
// ==/UserScript==

(function () {
    'use strict';
    if (history.scrollRestoration) {
        try {
            history.scrollRestoration = 'manual';
        } catch (ex) {
            // ignore error
        }
    }

    const observer = new MutationObserver(() => {
        const commentSection = document.querySelector('ytd-comments');
        if (commentSection) {
            observer.disconnect();
            attachListeners();
        }
    });

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

    function attachListeners() {
        document.body.addEventListener('click', function (e) {
            const link = e.target.closest('a');
            if (!link || !link.href || !link.href.includes('t=') || !link.href.includes('/watch?v=')) {
                return;
            }

            const commentsContainer = document.querySelector('ytd-comments');
            if (commentsContainer && commentsContainer.contains(link)) {
                e.preventDefault();
                e.stopPropagation();
                e.stopImmediatePropagation(); // key point:prevent other handlers from listening

                const url = new URL(link.href);
                const timeStr = url.searchParams.get('t');
                if (!timeStr) return;

                const seconds = parseTime(timeStr);
                if (isNaN(seconds)) return;

                const video = document.querySelector('video.html5-main-video');
                if (video) {
                    video.currentTime = seconds;
                    video.play().catch(() => {});
                }
            }
        }, true);
    }

    function parseTime(t) {
        if (!t) return 0;
        if (/^\d+$/.test(t)) {
            return parseInt(t, 10);
        }
        const regex = /(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s?)?/;
        const match = t.match(regex);
        if (!match) return 0;
        const h = parseInt(match[1] || '0', 10);
        const m = parseInt(match[2] || '0', 10);
        const s = parseInt(match[3] || '0', 10);
        return h * 3600 + m * 60 + s;
    }
})();