X/Twitter Ad Hider

Hides promoted tweets (ads) on the X/Twitter website.

  1. // ==UserScript==
  2. // @name X/Twitter Ad Hider
  3. // @name:zh-CN 隐藏 X/Twitter 广告
  4. // @name:zh-TW 隱藏 X/Twitter 廣告
  5. // @name:ja X/Twitter 広告非表示
  6. // @name:es Ocultar Anuncios de X/Twitter
  7. // @name:ar إخفاء إعلانات X/Twitter
  8. // @namespace http://tampermonkey.net/
  9. // @version 0.1.2
  10. // @description Hides promoted tweets (ads) on the X/Twitter website.
  11. // @description:zh-CN 在 X/Twitter 网站上隐藏推广推文(广告)。
  12. // @description:zh-TW 在 X/Twitter 網站上隱藏推廣推文(廣告)。
  13. // @description:ja X/Twitter ウェブサイト上のプロモーションツイート(広告)を非表示にします。
  14. // @description:es Oculta los tweets promocionados (anuncios) en el sitio web de X/Twitter.
  15. // @description:ar يخفي التغريدات المروجة (الإعلانات) على موقع X/Twitter.
  16. // @author Timothy Tao
  17. // @match https://twitter.com/*
  18. // @match https://x.com/*
  19. // @icon https://x.com/favicon.ico
  20. // @grant none
  21. // @run-at document-idle
  22. // @license MIT
  23. // ==/UserScript==
  24.  
  25. (function() {
  26. 'use strict';
  27.  
  28. // --- 配置 ---
  29. const TWEET_CONTAINER_SELECTOR = 'article[role="article"]';
  30. const AD_TEXTS = ['Ad'];
  31. const AD_DATA_TESTID_SELECTOR = '[data-testid="placementTracking"]';
  32. const TIMELINE_OBSERVER_TARGET_SELECTOR = 'main[role="main"]';
  33.  
  34. // --- 核心功能 ---
  35.  
  36. /**
  37. * 检查给定的推文元素是否是广告,如果是则隐藏它
  38. * @param {HTMLElement} tweetElement - 要检查的推文 <article> 元素
  39. */
  40. function checkAndHideAd(tweetElement) {
  41. if (tweetElement.style.display === 'none') {
  42. return;
  43. }
  44.  
  45. // 方法 1: 检查特定的 data-testid 属性
  46. const placementTrackingElement = tweetElement.querySelector(AD_DATA_TESTID_SELECTOR);
  47. if (placementTrackingElement) {
  48. // console.log('Hiding ad (data-testid):', tweetElement);
  49. hideElement(tweetElement);
  50. return;
  51. }
  52.  
  53. // 方法 2: 检查是否包含广告指示文本
  54. const spans = tweetElement.querySelectorAll('span');
  55. for (const span of spans) {
  56. const textContent = span.textContent?.trim();
  57. if (textContent && AD_TEXTS.some(adText => textContent.toLowerCase() === adText.toLowerCase())) {
  58. if (span.offsetParent !== null) { // 简单的可见性检查
  59. // console.log(`Hiding ad (text: ${textContent}):`, tweetElement);
  60. hideElement(tweetElement);
  61. return;
  62. }
  63. }
  64. }
  65. }
  66.  
  67. /**
  68. * 隐藏指定的元素
  69. * @param {HTMLElement} element
  70. */
  71. function hideElement(element) {
  72. element.style.setProperty('display', 'none', 'important');
  73. }
  74.  
  75. /**
  76. * 处理 DOM 变动,检查新增的节点是否包含广告推文
  77. * @param {MutationRecord[]} mutationsList
  78. * @param {MutationObserver} observer
  79. */
  80. function handleMutations(mutationsList, observer) {
  81. for (const mutation of mutationsList) {
  82. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  83. mutation.addedNodes.forEach(node => {
  84. if (node.nodeType === Node.ELEMENT_NODE) {
  85. if (node.matches && node.matches(TWEET_CONTAINER_SELECTOR)) {
  86. checkAndHideAd(node);
  87. } else { // 检查子节点,以防整个块被添加
  88. const potentialTweets = node.querySelectorAll(TWEET_CONTAINER_SELECTOR);
  89. potentialTweets.forEach(checkAndHideAd);
  90. }
  91. }
  92. });
  93. }
  94. }
  95. }
  96.  
  97. // --- 初始化观察者 ---
  98.  
  99. let observer = null;
  100.  
  101. function startObserver() {
  102. const targetNode = document.querySelector(TIMELINE_OBSERVER_TARGET_SELECTOR);
  103.  
  104. if (targetNode && !observer) {
  105. console.log('X/Twitter Ad Hider: Timeline found. Starting observer.');
  106.  
  107. const existingTweets = document.querySelectorAll(TWEET_CONTAINER_SELECTOR);
  108. existingTweets.forEach(checkAndHideAd);
  109.  
  110. observer = new MutationObserver(handleMutations);
  111. observer.observe(targetNode, { childList: true, subtree: true });
  112.  
  113. } else if (!targetNode) {
  114. // console.log('X/Twitter Ad Hider: Timeline target not found. Retrying in 500ms...');
  115. setTimeout(startObserver, 500);
  116. }
  117. }
  118.  
  119. // --- 脚本启动 ---
  120. console.log('X/Twitter Ad Hider: Script loaded. Waiting to start observer...');
  121. setTimeout(startObserver, 1000);
  122.  
  123. })();