您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically scrolls to the *next* matching video title, highlighting all matches. Search box, search on click, stop, animated border, no results. Handles dynamic loading.
当前为
// ==UserScript== // @name YouTube Grid Auto-Scroll & Search (Ultra Optimized - Instant) // @match https://www.youtube.com/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @version 2.7 // @description Automatically scrolls to the *next* matching video title, highlighting all matches. Search box, search on click, stop, animated border, no results. Handles dynamic loading. // @author Your Name (with further optimization) // @license MIT // @namespace https://gf.qytechs.cn/users/1435316 // ==/UserScript== (function() { 'use strict'; let targetText = ""; let searchBox; let isSearching = false; let searchInput; let searchButton; let stopButton; let searchTimeout; const SEARCH_TIMEOUT_MS = 20000; // 20 seconds const SCROLL_DELAY_MS = 750; const MAX_SEARCH_LENGTH = 255; let lastFoundIndex = -1; // Index of the last highlighted element let lastScrollHeight = 0; // Keep track of scroll height to detect end of content GM_addStyle(` /* Existing CSS (with additions) */ #floating-search-box { background-color: #222; padding: 5px; border: 1px solid #444; border-radius: 5px; display: flex; align-items: center; margin-left: 10px; } /* Responsive width for smaller screens */ @media (max-width: 768px) { #floating-search-box input[type="text"] { width: 150px; /* Smaller width on smaller screens */ } } #floating-search-box input[type="text"] { background-color: #333; color: #fff; border: 1px solid #555; padding: 3px 5px; border-radius: 3px; margin-right: 5px; width: 200px; height: 30px; } #floating-search-box input[type="text"]:focus { outline: none; border-color: #065fd4; } #floating-search-box button { background-color: #065fd4; color: white; border: none; padding: 3px 8px; border-radius: 3px; cursor: pointer; height: 30px; } #floating-search-box button:hover { background-color: #0549a8; } #floating-search-box button:focus { outline: none; } #stop-search-button { background-color: #aa0000; /* Red color */ } #stop-search-button:hover { background-color: #800000; } .highlighted-text { position: relative; /* Needed for the border to be positioned correctly */ z-index: 1; /* Ensure the border is on top of other elements */ } /* Creates the animated border effect */ .highlighted-text::before { content: ''; position: absolute; top: -2px; left: -2px; right: -2px; bottom: -2px; border: 2px solid transparent; /* Transparent border to start */ border-radius: 8px; /* Rounded corners */ background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet); /* Rainbow gradient */ background-size: 400% 400%; /* Make the gradient larger than the element */ animation: gradientAnimation 5s ease infinite; /* Animate the background position */ z-index: -1; /* Behind the content */ } /* Keyframes for the gradient animation */ @keyframes gradientAnimation { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Style for the error message */ #search-error-message { color: red; font-weight: bold; padding: 5px; position: fixed; top: 50px; left: 50%; transform: translateX(-50%); background-color: rgba(0, 0, 0, 0.8); color: white; border-radius: 5px; z-index: 10000; display: none; } /* Style for the no results message */ #search-no-results-message { color: #aaa; padding: 5px; position: fixed; top: 50px; left: 50%; transform: translateX(-50%); background-color: rgba(0, 0, 0, 0.8); border-radius: 5px; z-index: 10000; display: none; } `); // --- Create the Search Box --- function createSearchBox() { searchBox = document.createElement('div'); searchBox.id = 'floating-search-box'; searchBox.setAttribute('role', 'search'); // Add ARIA role for accessibility searchInput = document.createElement('input'); searchInput.type = 'text'; searchInput.placeholder = 'Search to scroll...'; searchInput.value = GM_getValue('lastSearchTerm', ''); searchInput.setAttribute('aria-label', 'Search within YouTube grid'); // ARIA label searchInput.maxLength = MAX_SEARCH_LENGTH; // Limit input length searchButton = document.createElement('button'); searchButton.textContent = 'Search'; searchButton.addEventListener('click', searchAndScroll); searchButton.setAttribute('aria-label', 'Start search'); // ARIA label stopButton = document.createElement('button'); stopButton.textContent = 'Stop'; stopButton.id = 'stop-search-button'; stopButton.addEventListener('click', stopSearch); stopButton.setAttribute('aria-label', 'Stop search'); // ARIA label searchBox.appendChild(searchInput); searchBox.appendChild(searchButton); searchBox.appendChild(stopButton); const mastheadEnd = document.querySelector('#end.ytd-masthead'); const buttonsContainer = document.querySelector('#end #buttons'); if (mastheadEnd) { if(buttonsContainer){ mastheadEnd.insertBefore(searchBox, buttonsContainer); } else{ mastheadEnd.appendChild(searchBox); } } else { console.error("Could not find the YouTube masthead's end element."); showErrorMessage("Could not find the YouTube masthead. Search box placed at top of page."); document.body.insertBefore(searchBox, document.body.firstChild); //fallback } // Trigger search on load if text is present AND we're on a videos page if (searchInput.value.trim() !== "" && window.location.href.includes("/videos")) { searchAndScroll(); } } // --- Show Error Message --- function showErrorMessage(message) { let errorDiv = document.getElementById('search-error-message'); if (!errorDiv) { errorDiv = document.createElement('div'); errorDiv.id = 'search-error-message'; document.body.appendChild(errorDiv); } errorDiv.textContent = message; errorDiv.style.display = 'block'; setTimeout(() => { errorDiv.style.display = 'none'; }, 5000); // Hide after 5 seconds } // --- Show "No Results" Message --- function showNoResultsMessage() { let noResultsDiv = document.getElementById('search-no-results-message'); if (!noResultsDiv) { noResultsDiv = document.createElement('div'); noResultsDiv.id = 'search-no-results-message'; noResultsDiv.textContent = "No matching results found."; document.body.appendChild(noResultsDiv); } noResultsDiv.style.display = 'block'; setTimeout(() => { noResultsDiv.style.display = 'none'; }, 5000); } // --- Stop Search Function --- function stopSearch() { if (observer) { observer.disconnect(); observer = null; // Ensure observer is nulled out } isSearching = false; clearTimeout(searchTimeout); // Reset the index when a new search starts lastFoundIndex = -1; } // --- Optimized Search and Scroll Function --- function searchAndScroll() { if (isSearching) return; isSearching = true; clearTimeout(searchTimeout); targetText = searchInput.value.trim().toLowerCase(); if (!targetText) { isSearching = false; return; } GM_setValue('lastSearchTerm', targetText); // Get all *visible* media elements const mediaElements = Array.from(document.querySelectorAll('ytd-rich-grid-media:not([style*="display: none"])')); // Find the next matching element, starting from lastFoundIndex + 1 let nextMatchIndex = -1; for (let i = lastFoundIndex + 1; i < mediaElements.length; i++) { const titleElement = mediaElements[i].querySelector('#video-title'); if (titleElement && titleElement.textContent.toLowerCase().includes(targetText)) { nextMatchIndex = i; break; // Stop searching once we find the *next* match } } // If a match is found, highlight it and scroll to it if (nextMatchIndex !== -1) { const matchElement = mediaElements[nextMatchIndex]; matchElement.classList.add('highlighted-text'); matchElement.scrollIntoView({ behavior: 'auto', block: 'center' }); lastFoundIndex = nextMatchIndex; isSearching = false; // Stop searching after finding a match } else { // No match found in the currently visible elements. Scroll down. lastScrollHeight = document.documentElement.scrollHeight; // Store current height window.scrollTo({ top: lastScrollHeight, behavior: 'auto' }); // Set a timeout to continue searching after a delay (to allow new content to load) searchTimeout = setTimeout(() => { if (!isSearching) return; // Check if searching was stopped // Check if we've reached the end of the page if (document.documentElement.scrollHeight === lastScrollHeight) { stopSearch(); showNoResultsMessage(); // No more content, and no match found } else { isSearching = false; // allow searchAndScroll to run it again searchAndScroll(); // Continue searching with newly loaded content } }, SCROLL_DELAY_MS); } // Set overall search timeout (for extreme cases) searchTimeout = setTimeout(() => { stopSearch(); showErrorMessage("Search timed out."); }, SEARCH_TIMEOUT_MS); } // --- Initialization --- createSearchBox(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址