Greasy Fork 还支持 简体中文。

YouTube Grid Row Controller

Add simple buttons to control items per row on Youtube's homepage grid, default is 4

目前為 2025-05-01 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name YouTube Grid Row Controller
  3. // @namespace https://github.com/HageFX-78
  4. // @version 0.1
  5. // @description Add simple buttons to control items per row on Youtube's homepage grid, default is 4
  6. // @author HageFX78
  7. // @license MIT
  8. // @match *://www.youtube.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. "use strict";
  16.  
  17. // If you have another script that disables or remove yt chips set this to false, true by default
  18. let embedInChips = true;
  19. // If you want to hide the controls/UI set this to true, false by default
  20. let hideControls = false;
  21. // This will be used if hideControls is true, 4 by default. Setting will affect the count when hideControls is true
  22. let defaultItemsPerRow = 4;
  23.  
  24. // Changing this will not effect the script, use the buttons to change it
  25. let itemsPerRow = GM_getValue("itemsPerRow", defaultItemsPerRow);
  26.  
  27. const overrideStyle = document.createElement("style");
  28. const staticOverrideStyle = document.createElement("style");
  29. const chipsStyle = document.createElement("style");
  30. const noChipsStyle = document.createElement("style");
  31.  
  32. chipsStyle.textContent = `
  33. #right-arrow {
  34. right: 10% !important;
  35. }
  36.  
  37. #chips-wrapper {
  38. justify-content: left !important;
  39. }
  40. #chips-content{
  41. width: 90% !important;
  42. }
  43.  
  44. #itemPerRowControl {
  45. display: flex;
  46. justify-content: center;
  47. align-items: center;
  48.  
  49. flex: 1;
  50. gap: 10px;
  51. box-sizing: border-box;
  52. user-select: none;
  53. }
  54.  
  55. `;
  56.  
  57. noChipsStyle.textContent = `
  58. #itemPerRowControl {
  59. display: flex;
  60. justify-content: right;
  61. align-items: center;
  62. margin: 0;
  63. padding: 0 60px;
  64.  
  65. width: 100%;
  66. gap: 10px;
  67. box-sizing: border-box;
  68. user-select: none;
  69. }
  70. `;
  71.  
  72. staticOverrideStyle.textContent = `
  73.  
  74. ytd-rich-item-renderer[rendered-from-rich-grid][is-in-first-column] {
  75. margin-left: calc(var(--ytd-rich-grid-item-margin) / 2) !important;
  76. }
  77.  
  78. #itemPerRowControl button {
  79. background-color: #f1f1f1;
  80. border: none;
  81. color: white;
  82. background-color:var(--yt-spec-badge-chip-background);
  83. font-size: 24px;
  84. text-align: center;
  85. display: inline-block;
  86.  
  87.  
  88. height: 30px;
  89. aspect-ratio: 1/1;
  90. border-radius: 50%;
  91. }
  92.  
  93. #itemPerRowControl button:hover {
  94. background-color: var(--yt-spec-button-chip-background-hover);
  95. cursor: pointer;
  96. }
  97. `;
  98.  
  99. overrideStyle.textContent = `
  100. ytd-rich-grid-renderer { --ytd-rich-grid-items-per-row: ${
  101. hideControls ? defaultItemsPerRow : itemsPerRow
  102. } !important; }
  103. `;
  104.  
  105. document.head.appendChild(staticOverrideStyle);
  106. document.head.appendChild(overrideStyle);
  107.  
  108. if (hideControls) {
  109. return;
  110. }
  111.  
  112. if (embedInChips) {
  113. document.head.appendChild(chipsStyle);
  114. waitForElement("#chips-wrapper").then((element) => {
  115. createControlDiv(element);
  116. });
  117. } else {
  118. document.head.appendChild(noChipsStyle);
  119. waitForElement("#contents").then((element) => {
  120. createControlDiv(element);
  121. });
  122. }
  123.  
  124. //-------------------------------------------- Functions --------------------------------------------------
  125. function waitForElement(selector) {
  126. return new Promise((resolve) => {
  127. const observer = new MutationObserver((mutations, observer) => {
  128. const element = document.querySelector(selector);
  129. if (element) {
  130. observer.disconnect();
  131. resolve(element);
  132. }
  133. });
  134.  
  135. observer.observe(document.body, {
  136. childList: true,
  137. subtree: true,
  138. });
  139. });
  140. }
  141.  
  142. function updateItemPerRow() {
  143. overrideStyle.textContent = `
  144. ytd-rich-grid-renderer { --ytd-rich-grid-items-per-row: ${itemsPerRow} !important; }`;
  145. GM_setValue("itemsPerRow", itemsPerRow); // Save the value
  146. }
  147.  
  148. function createControlDiv(insertTarget) {
  149. let controlDiv = document.createElement("div");
  150. controlDiv.id = "itemPerRowControl";
  151. controlDiv.classList.add("style-scope", "ytd-rich-grid-renderer");
  152.  
  153. // Control
  154. let addItemPerRow = document.createElement("button");
  155. addItemPerRow.innerText = "+";
  156. addItemPerRow.addEventListener("click", () => {
  157. itemsPerRow++;
  158. updateItemPerRow();
  159. });
  160.  
  161. let minusItemPerRow = document.createElement("button");
  162. minusItemPerRow.innerText = "-";
  163. minusItemPerRow.addEventListener("click", () => {
  164. if (itemsPerRow > 1) {
  165. itemsPerRow--;
  166. updateItemPerRow();
  167. }
  168. });
  169.  
  170. controlDiv.appendChild(minusItemPerRow);
  171. controlDiv.appendChild(addItemPerRow);
  172.  
  173. if (embedInChips) {
  174. insertTarget.appendChild(controlDiv);
  175. } else {
  176. insertTarget.parentNode.insertBefore(controlDiv, insertTarget);
  177. }
  178. }
  179. })();