V2Block

在 V 站屏蔽某个帖子

  1. // ==UserScript==
  2. // @name V2Block
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.2
  5. // @description 在 V 站屏蔽某个帖子
  6. // @author hudidit
  7. // @match https://v2ex.com/*
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. console.log('[v2block]');
  16.  
  17. // ********** 初始化逻辑 **********
  18.  
  19. const STORAGE_LINKS = 'v2_menu_block_links';
  20. let blockedLinks = getLink().links;
  21. hideListItem();
  22.  
  23. // 菜单
  24. const menu = document.createElement('div');
  25. menu.id = 'v2-menu-container';
  26. document.body.appendChild(menu);
  27. menu.innerHTML = `
  28. <a class="v2-menu-item" id="v2-menu-block">不看这个页面</a>
  29. <a class="v2-menu-item" id="v2-menu-unblock">恢复全部页面</a>
  30. <a class="v2-menu-item" id="v2-menu-new-tab" target="_blank">在新标签页打开</a>
  31. `;
  32. const menuState = {
  33. href: '',
  34. left: -999,
  35. top: -999,
  36. linkPath: '',
  37. linkText: '',
  38. visible: false,
  39. };
  40.  
  41. // 蒙层
  42. const overlay = document.createElement('div');
  43. overlay.id = 'v2-menu-overlay';
  44. document.body.appendChild(overlay);
  45.  
  46. appendStyle();
  47. resetState();
  48. console.log('[v2block] blocked links:', blockedLinks);
  49.  
  50. // ********** 监听事件 **********
  51.  
  52. document.addEventListener('contextmenu', function(e) {
  53. const el = e.target;
  54. if (el.tagName.toUpperCase() === 'A' && el.className === 'topic-link') {
  55. e.preventDefault();
  56. const href = el.getAttribute('href');
  57. const linkPath = getPathFormHref(href);
  58. updateState({
  59. visible: true,
  60. href,
  61. left: e.clientX,
  62. top: e.clientY,
  63. linkPath,
  64. linkText: el.textContent,
  65. });
  66. }
  67. });
  68.  
  69. overlay.addEventListener('click', function() {
  70. resetState();
  71. });
  72.  
  73. document.querySelector('#v2-menu-block').addEventListener('click', function() {
  74. const {
  75. linkPath,
  76. linkText,
  77. } = menuState;
  78. saveLink({
  79. path: linkPath,
  80. text: linkText,
  81. });
  82. hideListItem();
  83. resetState();
  84. });
  85.  
  86. document.querySelector('#v2-menu-unblock').addEventListener('click', function() {
  87. clearLink();
  88. // TODO: 刷新页面,比较粗暴,需要优化
  89. location.reload();
  90. });
  91.  
  92. // ********** 定义函数 **********
  93.  
  94. function resetState() {
  95. updateState({
  96. visible: false,
  97. href: '',
  98. linkText: '',
  99. });
  100. }
  101.  
  102. function appendStyle() {
  103. const styleEl = document.createElement('style');
  104. styleEl.innerText = `
  105. #v2-menu-overlay {
  106. position: fixed;
  107. width: 100%;
  108. height: 100%;
  109. left: 0;
  110. top: 0;
  111. z-index: 1;
  112. display: none;
  113. }
  114. #v2-menu-container {
  115. position: fixed;
  116. z-index: 2;
  117. min-width: 200px;
  118. background: #fff;
  119. border: 1px solid #c0c0c0;
  120. box-shadow: 0 0 5px rgba(136, 136, 136, 0.3);
  121. }
  122. #v2-menu-container .v2-menu-item {
  123. display: block;
  124. font-size: 14px;
  125. line-height: 30px;
  126. padding: 0 10px;
  127. border-bottom: 1px solid #e0e0e0;
  128. color: #333;
  129. text-decoration: none;
  130. cursor: pointer;
  131. }
  132. #v2-menu-container .v2-menu-item:hover {
  133. background: #e0e0e0;
  134. }
  135. #v2-menu-container .v2-menu-item:last-of-type {
  136. border-bottom: none;
  137. }
  138. `;
  139. document.querySelector('head').appendChild(styleEl);
  140. }
  141.  
  142. /**
  143. * 更新状态
  144. * @param {object} state 需要更新的状态字段
  145. */
  146. function updateState(state = {}) {
  147. Object.assign(menuState, state);
  148.  
  149. const {
  150. visible,
  151. href = '',
  152. left,
  153. top,
  154. } = menuState;
  155.  
  156. if (!visible) {
  157. hideMenu();
  158. return;
  159. }
  160.  
  161. showMenu();
  162.  
  163. document.querySelector('#v2-menu-new-tab').href = href;
  164. menu.style.left = `${left}px`;
  165. menu.style.top = `${top}px`;
  166. }
  167.  
  168. function showMenu(state = {}) {
  169. overlay.style.display = 'block';
  170. menu.style.display = 'block';
  171. }
  172.  
  173. function hideMenu() {
  174. overlay.style.display = 'none';
  175. menu.style.display = 'none';
  176. }
  177.  
  178. function hideListItem() {
  179. const items = document.querySelectorAll('#Main .cell.item');
  180. for (let item of items) {
  181. const topic = item.querySelector('.topic-link');
  182. if (blockedLinks.find(link => link.path === getPathFormHref(topic.getAttribute('href')))) {
  183. item.remove();
  184. }
  185. }
  186. }
  187.  
  188. function saveLink(link = { path: '', text: '' }) {
  189. const data = JSON.parse(GM_getValue(STORAGE_LINKS, '{}'));
  190. data.links = data.links || [];
  191. const links = data.links;
  192. const isAlreadyIn = links.findIndex(item => item.path === link.path) > -1;
  193. if (isAlreadyIn) return;
  194. links.push(link);
  195. blockedLinks = data.links;
  196. GM_setValue(STORAGE_LINKS, JSON.stringify(data));
  197. }
  198.  
  199. function getLink() {
  200. const data = JSON.parse(GM_getValue(STORAGE_LINKS, '{}'));
  201. data.links = data.links || [];
  202. return data;
  203. }
  204.  
  205. function clearLink() {
  206. const data = {};
  207. data.links = [];
  208. blockedLinks = data.links;
  209. GM_setValue(STORAGE_LINKS, JSON.stringify(data));
  210. }
  211.  
  212. function getPathFormHref(href = '') {
  213. return href.split('#')[0];
  214. }
  215.  
  216. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址