您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds simple buttons to control items per row on Youtube's home feed, works for shorts and news sections too. Buttons can be hidden if needed.
- // ==UserScript==
- // @name YouTube Grid Row Controller
- // @namespace https://github.com/HageFX-78
- // @version 0.5
- // @description Adds simple buttons to control items per row on Youtube's home feed, works for shorts and news sections too. Buttons can be hidden if needed.
- // @author HageFX78
- // @license MIT
- // @match *://www.youtube.com/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
- // @grant GM_setValue
- // @grant GM_getValue
- // ==/UserScript==
- (function () {
- 'use strict';
- // Configurable options
- const embedInChips = true; // Only applies to the one that is attached to the categories bar, set false if you have another script that removes the bar
- const hideControls = false; // set true to hide UI controls, it will use the default values instead
- const transparentButtons = false; // set true to make the buttons transparent and less intrusive, only applies if hideControls is false
- const defaultSettingValue = {
- // Default values mainly used when if you want to hide the buttons, change the values to your liking
- content: 4,
- news: 5,
- shorts: 6,
- };
- let currentSettingValues = {
- content: GM_getValue('itemPerRow', defaultSettingValue.content),
- news: GM_getValue('newsPerRow', defaultSettingValue.news),
- shorts: GM_getValue('shortsPerRow', defaultSettingValue.shorts),
- };
- // Styles
- const style = (css) => {
- const el = document.createElement('style');
- el.textContent = css;
- document.head.appendChild(el);
- return el;
- };
- style(`
- ${
- hideControls
- ? ''
- : '#right-arrow {right: 10% !important;} #chips-wrapper {justify-content: left !important;}#chips-content{width: 90% !important;}'
- }
- .justify-left-custom {
- justify-content: left !important;
- }
- ytd-rich-item-renderer[rendered-from-rich-grid][is-in-first-column] {
- margin-left: calc(var(--ytd-rich-grid-item-margin) / 2) !important;
- }
- ytd-rich-item-renderer[hidden][is-responsive-grid], [is-slim-media]{
- display: block !important;
- }
- ytd-rich-item-renderer{
- margin-bottom: var(--ytd-rich-grid-row-margin) !important;
- }
- .button-container.ytd-rich-shelf-renderer {
- display: none !important;
- }
- #dismissible.ytd-rich-shelf-renderer {
- padding-bottom: 0 !important;
- border-bottom: none !important;
- }
- .itemPerRowControl {
- display: flex;
- justify-content: right;
- align-items: center;
- flex: 1;
- gap: 10px;
- box-sizing: border-box;
- user-select: none;
- ${embedInChips ? '' : 'width: 100%;'};
- }
- .itemPerRowControl button {
- border: none;
- color: var(--yt-spec-text-primary);
- background-color:${transparentButtons ? 'transparent' : 'var(--yt-spec-badge-chip-background)'};
- font-size: 24px;
- text-align: center;
- display: inline-block;
- height: 30px;
- aspect-ratio: 1/1;
- border-radius: 50%;
- }
- .itemPerRowControl button:hover {
- background-color: var(--yt-spec-button-chip-background-hover);
- cursor: pointer;
- }
- `);
- const dynamicStyle = style('');
- function updatePageLayout() {
- dynamicStyle.textContent = `
- ytd-rich-grid-renderer {
- --ytd-rich-grid-items-per-row: ${hideControls ? defaultSettingValue.content : currentSettingValues.content} !important;
- }
- ytd-rich-shelf-renderer:not([is-shorts]) {
- --ytd-rich-grid-items-per-row: ${hideControls ? defaultSettingValue.news : currentSettingValues.news} !important;
- }
- ytd-rich-shelf-renderer[is-shorts] {
- --ytd-rich-grid-slim-items-per-row: ${hideControls ? defaultSettingValue.shorts : currentSettingValues.shorts} !important;
- --ytd-rich-grid-items-per-row: ${hideControls ? defaultSettingValue.shorts : currentSettingValues.shorts} !important;
- }
- `;
- }
- function saveValues() {
- GM_setValue('itemPerRow', currentSettingValues.content);
- GM_setValue('newsPerRow', currentSettingValues.news);
- GM_setValue('shortsPerRow', currentSettingValues.shorts);
- }
- function updateAndSave() {
- updatePageLayout();
- saveValues();
- }
- function waitForElement(baseQuery, selector) {
- return new Promise((resolve) => {
- const observer = new MutationObserver(() => {
- const el = baseQuery.querySelector(selector);
- if (el) {
- observer.disconnect();
- resolve(el);
- }
- });
- observer.observe(baseQuery, { childList: true, subtree: true });
- });
- }
- function watchMainContent(container) {
- const observer = new MutationObserver((mutations) => {
- mutations.forEach(({ addedNodes }) => {
- for (let node of addedNodes) {
- if (node.nodeType === 1 && node.matches('ytd-rich-section-renderer')) {
- const ref = node.querySelector('#menu-container');
- const hasControlDiv = node.querySelector('.itemPerRowControl') !== null;
- const isShorts = node.querySelector('[is-shorts]') !== null;
- if (hasControlDiv) continue; // Skip if already exists
- createControlDiv(ref, isShorts ? 'shorts' : 'news', true);
- }
- }
- });
- });
- observer.observe(container, { childList: true, subtree: true });
- }
- function createControlDiv(target, type, insertBefore = false) {
- const controlDiv = document.createElement('div');
- controlDiv.classList.add('style-scope', 'ytd-rich-grid-renderer', 'itemPerRowControl');
- ['-', '+'].forEach((symbol) => {
- const btn = document.createElement('button');
- btn.innerText = symbol;
- btn.addEventListener('click', () => {
- if (symbol === '+') {
- currentSettingValues[type]++;
- console.log(currentSettingValues[type]);
- } else if (currentSettingValues[type] > 1) {
- currentSettingValues[type]--;
- }
- updateAndSave();
- });
- controlDiv.appendChild(btn);
- });
- if (insertBefore) target.parentNode.insertBefore(controlDiv, target);
- else target.appendChild(controlDiv);
- if (!insertBefore) controlDiv.classList.add('justify-left-custom');
- }
- function init(queryStartLocation) {
- updatePageLayout();
- if (hideControls) return;
- if (embedInChips) {
- waitForElement(queryStartLocation, '#chips-wrapper').then((el) => createControlDiv(el, 'content'));
- } else {
- waitForElement(queryStartLocation, '#contents.ytd-rich-grid-renderer').then((el) => createControlDiv(el, 'content', true));
- }
- // Start watching for newly loaded sections
- waitForElement(queryStartLocation, '#contents.ytd-rich-grid-renderer').then(watchMainContent);
- // Cleanup after init
- window.removeEventListener('yt-navigate-finish', handlePageContentChanged);
- }
- // Workaround when reloaded on creator's home page and going back to main page will hide the buttons
- let firstLoad = true;
- function handlePageContentChanged() {
- if (location.href.endsWith('youtube.com/')) {
- let browseElements = document.querySelectorAll('ytd-browse');
- if (firstLoad || browseElements.length <= 1) {
- init(browseElements[0]);
- } else {
- // If reloaded on creator's home page, second ytd-browse will be the main page
- init(browseElements[1]);
- }
- }
- firstLoad = false;
- }
- // ----------------------------------- Main Execution -----------------------------------
- window.addEventListener('yt-navigate-finish', handlePageContentChanged);
- })();