您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically expands the "Show more" link in the left sidebar Subscriptions on YouTube, and keeps it expanded across navigations.
// ==UserScript== // @name YouTube Always Show More (Subscriptions) // @namespace https://yourscripts.example // @version 1.2.0 // @license MIT // @description Automatically expands the "Show more" link in the left sidebar Subscriptions on YouTube, and keeps it expanded across navigations. // @author ezzdev // @match https://www.youtube.com/* // @run-at document-idle // @grant none // ==/UserScript== (function() { 'use strict'; // Debounce helper const debounce = (fn, wait = 200) => { let t; return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), wait); }; }; // Utility: find potential "Show more" toggles inside the left guide function findShowMoreButtons(guideRoot) { if (!guideRoot) return []; const candidates = []; // 1) Buttons/controls that are collapsed via aria-expanded candidates.push(...guideRoot.querySelectorAll( 'button[aria-expanded="false"], [role="button"][aria-expanded="false"]' )); // 2) Buttons with an aria-label or title that mentions "Show more" (case-insensitive) candidates.push(...Array.from(guideRoot.querySelectorAll('button, [role="button"], yt-button-shape, tp-yt-paper-item')).filter(el => { const label = (el.getAttribute?.('aria-label') || el.getAttribute?.('title') || '').trim().toLowerCase(); return label.includes('show more') || label.includes('more'); })); // 3) Fallback: elements whose visible text says "Show more" (case-insensitive) candidates.push(...Array.from(guideRoot.querySelectorAll('button, [role="button"], yt-button-shape, tp-yt-paper-item')).filter(el => { const text = (el.textContent || '').trim().toLowerCase(); // keep it loose but not too loose return text === 'show more' || text === 'more' || text.startsWith('show more'); })); // Only keep ones that are actually visible const visible = candidates.filter(el => { const rect = el.getBoundingClientRect?.(); const style = window.getComputedStyle?.(el); return rect && rect.width > 0 && rect.height > 0 && style && style.visibility !== 'hidden' && style.display !== 'none'; }); // Dedupe return Array.from(new Set(visible)); } // Try to identify the left guide root (desktop #guide; fallback to drawer guide) function getGuideRoot() { return document.querySelector('#guide') || document.querySelector('ytd-guide-renderer') || document.querySelector('ytd-mini-guide-renderer') || null; } let lastClickTs = 0; function ensureExpanded() { const now = Date.now(); // throttle: no more than once every 500ms if (now - lastClickTs < 500) return; const guideRoot = getGuideRoot(); if (!guideRoot) return; // We only want the Subscriptions "Show more" in the left guide. // Click any collapsed toggles we can find; usually there is only one relevant. const buttons = findShowMoreButtons(guideRoot); // Prefer ones that live inside a Subscriptions section if we can spot it const prioritized = buttons.sort((a, b) => { const aScore = scoreButton(a); const bScore = scoreButton(b); return bScore - aScore; // higher score first }); for (const btn of prioritized) { try { btn.click(); lastClickTs = Date.now(); // Small break in case there are multiple collapses break; } catch (_) {} } } // Heuristic: reward buttons near text that says "Subscriptions" (any language-safe-ish hint) function scoreButton(el) { let score = 0; // aria-expanded false is a good sign if (el.getAttribute('aria-expanded') === 'false') score += 3; // Is it in the guide? if (el.closest('#guide, ytd-guide-renderer, ytd-mini-guide-renderer')) score += 2; // Nearby text “Subscriptions” const nearbyText = (el.closest('ytd-guide-section-renderer') || el.parentElement)?.textContent?.toLowerCase() || ''; if (/\bsubscription/.test(nearbyText)) score += 3; // Label hints const label = (el.getAttribute('aria-label') || el.getAttribute('title') || el.textContent || '').toLowerCase(); if (label.includes('show more')) score += 2; if (label === 'more' || label.includes('more')) score += 1; return score; } // Observe changes in the guide so we can re-expand when YouTube re-renders const observeGuide = debounce(() => { const guideRoot = getGuideRoot(); if (!guideRoot) return; ensureExpanded(); // MutationObserver to catch SPA updates or reflows of the guide section const mo = new MutationObserver(debounce(() => { ensureExpanded(); }, 200)); mo.observe(guideRoot, { childList: true, subtree: true }); }, 250); // Hook into YouTube’s SPA navigation events if present window.addEventListener('yt-navigate-finish', () => { observeGuide(); // Run a few times after navigation just in case setTimeout(ensureExpanded, 200); setTimeout(ensureExpanded, 600); setTimeout(ensureExpanded, 1200); }, { passive: true }); // Initial attempts after page ready const kickOff = () => { observeGuide(); // Try a few times early in case the guide loads late let tries = 0; const t = setInterval(() => { ensureExpanded(); tries++; if (tries >= 10) clearInterval(t); }, 400); }; if (document.readyState === 'complete' || document.readyState === 'interactive') { kickOff(); } else { window.addEventListener('DOMContentLoaded', kickOff, { once: true }); } })();