YouTube Key Navigation

Add fast, customizable keyboard shortcuts to YouTube. Instantly open Home, Watch Later, and History, or perform actions like Like, Dislike, Subscribe, Toggle Notifications, and Voice Search — all without refreshing the page. Fully configurable keybindings for seamless YouTube navigation and control.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         YouTube Key Navigation
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Add fast, customizable keyboard shortcuts to YouTube. Instantly open Home, Watch Later, and History, or perform actions like Like, Dislike, Subscribe, Toggle Notifications, and Voice Search — all without refreshing the page. Fully configurable keybindings for seamless YouTube navigation and control.
// @icon               https://www.google.com/s2/favicons?sz=64&domain=youtube.com

// @author       Anurag Kashyap
// @match        *://www.youtube.com/*
// @match        *://youtube.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    /**********************************************************
     * 🛠 KEYBIND CONFIG - EDIT THIS PART ONLY
     *
     * Just change the keys in the arrays below.
     * Use lowercase for convenience; script will match both
     * lowercase and uppercase automatically.
     **********************************************************/
    const KEYBINDS = {
        home: ['u'],           // h → go to Home
        watchLater: ['q'],     // . → go to Watch Later
        history: ['h'],        // q → go to History
        notifications: ['z'],  // z → toggle notifications
        voiceSearch: ['x'],    // x → search with your voice
        like: ['b'],           // v → like video
        dislike: ['n'],        // n → dislike video
        subscribe: ['e'],      // b → subscribe to channel
    };
    /**********************************************************/

    // Utility: Check if user is typing in an input/textarea/contenteditable
    function isTypingInInput(e) {
        const target = e.target;
        if (!target) return false;

        const tag = target.tagName;
        const editable = target.isContentEditable;

        return (
            editable ||
            tag === 'INPUT' ||
            tag === 'TEXTAREA' ||
            tag === 'SELECT'
        );
    }

    // Utility: Try multiple selectors until one works
    function clickFirstSelector(selectors) {
        for (const selector of selectors) {
            const el = document.querySelector(selector);
            if (el) {
                el.click();
                return true;
            }
        }
        return false;
    }

    // NAVIGATION HELPERS
    function goHome() {
        const success = clickFirstSelector([
            'a[title="Home"]',
            'a[aria-label="Home"]',
            'a[endpoint*="FEwhat_to_watch"]',
            'a[href="/"]'
        ]);

        if (!success) console.warn('Home link not found.');
    }

    function goWatchLater() {
        const success = clickFirstSelector([
            'a[title="Watch later"]',
            'a[aria-label^="Watch later"]',
            'a[href*="&list=WL"]',
            'a[href*="playlist?list=WL"]'
        ]);

        if (!success) console.warn('Watch later link not found.');
    }

    function goHistory() {
        const success = clickFirstSelector([
            'a[title="History"]',
            'a[aria-label="History"]',
            'a[href="/feed/history"]'
        ]);

        if (!success) console.warn('History link not found.');
    }

    // ACTION HELPERS
   function toggleNotifications() {
    // Prefer the top-right notifications bell in the masthead
    let btn = document.querySelector('ytd-notification-topbar-button-renderer button');

    // Fallback: search all "Notifications" buttons but keep only the one in the topbar
    if (!btn) {
        const candidates = Array.from(
            document.querySelectorAll('button[aria-label*="Notification"]')
        );

        btn = candidates.find(el =>
            el.closest('ytd-notification-topbar-button-renderer') ||
            el.closest('ytd-masthead')
        );
    }

    if (btn) {
        btn.click();
    } else {
        console.warn('Topbar notification button not found.');
    }
}


    function voiceSearch() {
        const success = clickFirstSelector([
            'button[aria-label="Search with your voice"]',
            'button[aria-label*="voice"]',
            'ytd-microphone-button button'
        ]);

        if (!success) console.warn('Voice search button not found.');
    }

    function likeVideo() {
        const success = clickFirstSelector([
            'ytd-segmented-like-dislike-button-renderer yt-button-shape:nth-of-type(1) button',
            'button[aria-label^="Like this video"]',
            'button[aria-pressed][aria-label^="like this video" i]'
        ]);

        if (!success) console.warn('Like button not found.');
    }

    function dislikeVideo() {
        const success = clickFirstSelector([
            'ytd-segmented-like-dislike-button-renderer yt-button-shape:nth-of-type(2) button',
            'button[aria-label^="Dislike this video"]',
            'button[aria-pressed][aria-label^="dislike this video" i]'
        ]);

        if (!success) console.warn('Dislike button not found.');
    }

    function subscribeChannel() {
        const btn = document.querySelector(
            'ytd-subscribe-button-renderer tp-yt-paper-button,' +
            'ytd-subscribe-button-renderer button,' +
            'button[aria-label^="Subscribe"],' +
            'button[aria-label^="Subscribed"]'
        );

        if (!btn) {
            console.warn('Subscribe button not found.');
            return;
        }

        const pressed = btn.getAttribute('aria-pressed');
        if (pressed === 'true') {
            console.log('Already subscribed (aria-pressed=true). No action taken.');
            return;
        }

        btn.click();
    }

    // Build key → action map from KEYBINDS config
    const keyToAction = {};
    function registerKeys() {
        const actions = {
            home: goHome,
            watchLater: goWatchLater,
            history: goHistory,
            notifications: toggleNotifications,
            voiceSearch: voiceSearch,
            like: likeVideo,
            dislike: dislikeVideo,
            subscribe: subscribeChannel,
        };

        for (const [actionName, keys] of Object.entries(KEYBINDS)) {
            const handler = actions[actionName];
            if (!handler) continue;

            keys.forEach(k => {
                if (!k) return;
                const key = String(k).toLowerCase();
                keyToAction[key] = handler;
            });
        }
    }

    registerKeys();

    // KEY LISTENER
    window.addEventListener('keydown', function (e) {
        if (!/\.youtube\.com$/.test(location.hostname)) return;
        if (e.altKey || e.ctrlKey || e.metaKey) return;
        if (isTypingInInput(e)) return;

        const key = e.key.toLowerCase();
        const action = keyToAction[key];

        if (action) {
            e.preventDefault();
            action();
        }
    });
})();