Greasy Fork

Greasy Fork is available in English.

Twitter/X 稳定屏蔽广告推文

避免页面崩溃的动态广告推文识别与隐藏逻辑(推荐标签识别)

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Twitter/X 稳定屏蔽广告推文
// @namespace    https://x.com
// @version      1.8
// @description  避免页面崩溃的动态广告推文识别与隐藏逻辑(推荐标签识别)
// @author       _Sure.Lee
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const OBSERVER_CONFIG = { childList: true, subtree: true };

  function isPromoted(tweet) {
    const labels = tweet.querySelectorAll('div[dir="ltr"]');
    return Array.from(labels).some((el) =>
      ['推荐', 'Promoted', '推薦', '推广'].some(keyword => el.textContent.trim().includes(keyword))
    );
  }

  function hideWithAnimation(tweet) {
    if (tweet.dataset.__adHandled) return;
    tweet.dataset.__adHandled = 'true';

    tweet.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
    tweet.style.opacity = '0';
    tweet.style.transform = 'scale(0.95)';

    setTimeout(() => {
      try {
        tweet.style.display = 'none'; // 更安全:不直接 remove
        console.log('🧹 屏蔽广告推文成功');
      } catch (e) {
        console.warn('❗️ 删除广告推文失败:', e);
      }
    }, 600);
  }

  const visibilityObserver = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        const tweet = entry.target;
        if (isPromoted(tweet)) {
          console.log('👀 发现广告推文,准备屏蔽...');
          requestIdleCallback(() => hideWithAnimation(tweet), { timeout: 1000 });
          visibilityObserver.unobserve(tweet);
        }
      }
    });
  });

  function scanTweets() {
    const tweets = document.querySelectorAll('article[data-testid="tweet"]');
    tweets.forEach((tweet) => {
      if (!tweet.dataset.__observing) {
        tweet.dataset.__observing = 'true';
        visibilityObserver.observe(tweet);
      }
    });
  }

  // 启动 MutationObserver 监听动态加载
  const mo = new MutationObserver(() => {
    requestIdleCallback(scanTweets, { timeout: 500 });
  });

  mo.observe(document.body, OBSERVER_CONFIG);

  // 首次执行
  scanTweets();
})();