隱藏 X/Twitter 廣告

在 X/Twitter 網站上隱藏推廣推文(廣告)。

目前為 2025-04-08 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         X/Twitter Ad Hider
// @name:zh-CN   隐藏 X/Twitter 广告
// @name:zh-TW   隱藏 X/Twitter 廣告
// @name:ja      X/Twitter 広告非表示
// @name:es      Ocultar Anuncios de X/Twitter
// @name:ar      إخفاء إعلانات X/Twitter
// @namespace    http://tampermonkey.net/
// @version      0.1.1-alpha
// @description  Hides promoted tweets (ads) on the X/Twitter website.
// @description:zh-CN 在 X/Twitter 网站上隐藏推广推文(广告)。
// @description:zh-TW 在 X/Twitter 網站上隱藏推廣推文(廣告)。
// @description:ja      X/Twitter ウェブサイト上のプロモーションツイート(広告)を非表示にします。
// @description:es      Oculta los tweets promocionados (anuncios) en el sitio web de X/Twitter.
// @description:ar      يخفي التغريدات المروجة (الإعلانات) على موقع X/Twitter.
// @author       Your Name (or AI)
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- 配置 ---
    const TWEET_CONTAINER_SELECTOR = 'article[role="article"]';
    const AD_TEXTS = ['Ad']; 
    const AD_DATA_TESTID_SELECTOR = '[data-testid="placementTracking"]';
    const TIMELINE_OBSERVER_TARGET_SELECTOR = 'main[role="main"]';

    // --- 核心功能 ---

    /**
     * 检查给定的推文元素是否是广告,如果是则隐藏它
     * @param {HTMLElement} tweetElement - 要检查的推文 <article> 元素
     */
    function checkAndHideAd(tweetElement) {
        if (tweetElement.style.display === 'none') {
            return;
        }

        // 方法 1: 检查特定的 data-testid 属性
        const placementTrackingElement = tweetElement.querySelector(AD_DATA_TESTID_SELECTOR);
        if (placementTrackingElement) {
            // console.log('Hiding ad (data-testid):', tweetElement);
            hideElement(tweetElement);
            return;
        }

        // 方法 2: 检查是否包含广告指示文本
        const spans = tweetElement.querySelectorAll('span');
        for (const span of spans) {
            const textContent = span.textContent?.trim();
            if (textContent && AD_TEXTS.some(adText => textContent.toLowerCase() === adText.toLowerCase())) {
                if (span.offsetParent !== null) { // 简单的可见性检查
                    // console.log(`Hiding ad (text: ${textContent}):`, tweetElement);
                    hideElement(tweetElement);
                    return;
                }
            }
        }
    }

    /**
     * 隐藏指定的元素
     * @param {HTMLElement} element
     */
    function hideElement(element) {
        element.style.setProperty('display', 'none', 'important');
    }

    /**
     * 处理 DOM 变动,检查新增的节点是否包含广告推文
     * @param {MutationRecord[]} mutationsList
     * @param {MutationObserver} observer
     */
    function handleMutations(mutationsList, observer) {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.matches && node.matches(TWEET_CONTAINER_SELECTOR)) {
                            checkAndHideAd(node);
                        } else { // 检查子节点,以防整个块被添加
                            const potentialTweets = node.querySelectorAll(TWEET_CONTAINER_SELECTOR);
                            potentialTweets.forEach(checkAndHideAd);
                        }
                    }
                });
            }
        }
    }

    // --- 初始化观察者 ---

    let observer = null;

    function startObserver() {
        const targetNode = document.querySelector(TIMELINE_OBSERVER_TARGET_SELECTOR);

        if (targetNode && !observer) {
            console.log('X/Twitter Ad Hider: Timeline found. Starting observer.');

            const existingTweets = document.querySelectorAll(TWEET_CONTAINER_SELECTOR);
            existingTweets.forEach(checkAndHideAd);

            observer = new MutationObserver(handleMutations);
            observer.observe(targetNode, { childList: true, subtree: true });

        } else if (!targetNode) {
            // console.log('X/Twitter Ad Hider: Timeline target not found. Retrying in 500ms...');
            setTimeout(startObserver, 500);
        }
    }

    // --- 脚本启动 ---
    console.log('X/Twitter Ad Hider: Script loaded. Waiting to start observer...');
    setTimeout(startObserver, 1000);

})();