Greasy Fork

Greasy Fork is available in English.

推特(x.com)网页端清理脚本

隐藏/删除推特时间线中的推荐内容和侧边栏推荐

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         推特(x.com)网页端清理脚本
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  隐藏/删除推特时间线中的推荐内容和侧边栏推荐
// @author       You
// @match        https://x.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    let isExecuting = false;
    let processedElements = new Set();
    let lastScrollTop = 0;
    let scrollCheckInterval = null;

    //匹配 x.com/abc123 个人主页,也匹配了平台主页 x.com/home
    let userPattern = /^\/([^\/?#]+)$/;

    //匹配 x.com/abc123/1234567890 ,不匹配 x.com/abc123/with_replies
    let statusPattern = /^\/[^\/?#]+\/status\/\d+$/;

    function 隐藏status发现更多之后的推荐() {
        console.log('执行/status/页清理操作,隐藏"发现更多"之后的推荐');
        const targetSpan = Array.from(document.querySelectorAll('span'))
            .find(span => span.textContent.includes('发现更多'));
        if (!targetSpan) return false;

        const outerDiv = targetSpan.closest('div[style*="translateY"]');
        if (!outerDiv) return false;

        // 获取目标元素的translateY值作为参考点
        const style = outerDiv.getAttribute('style');
        const translateYMatch = style.match(/translateY\(([^)]+)\)/);
        if (!translateYMatch) return false;

        const targetTranslateY = parseFloat(translateYMatch[1].replace('px', ''));

        // 查找并隐藏所有translateY值大于目标值的div中的推荐内容
        let 隐藏计数 = 0;
        const allDivs = document.querySelectorAll('div[style*="translateY"]');

        allDivs.forEach(div => {
            const divStyle = div.getAttribute('style');
            const divTranslateYMatch = divStyle.match(/translateY\(([^)]+)\)/);
            if (divTranslateYMatch) {
                const divTranslateY = parseFloat(divTranslateYMatch[1].replace('px', ''));
                if (divTranslateY > targetTranslateY) {
                    const targetChild = div.querySelector('div > div > article > div > div > div:nth-child(2)');
                    if (targetChild && targetChild.offsetParent !== null) { // 检查元素是否可见
                        targetChild.style.display = 'none';
                        隐藏计数++;
                    }
                }
            }
        });

        if (隐藏计数 > 0) {
            console.log(`已隐藏 ${隐藏计数} 个推荐内容`);
            return true;
        }

        return false;
    }

    function 隐藏主页时间线中的转发(用户名){
        const tweetContainers = document.querySelectorAll('div[data-testid="cellInnerDiv"]');
        let hiddenCount = 0;

        tweetContainers.forEach(container => {
            const isRetweet = container.querySelector('[data-testid="socialContext"]');
            if (!isRetweet) return;

            const userAvatar = container.querySelector(`[data-testid="UserAvatar-Container-${用户名}"]`);
            if (!userAvatar) {
                // 隐藏而不是删除,避免破坏布局
                container.style.display = 'none';
                container.setAttribute('data-hidden', 'true');
                hiddenCount++;
            }
        });

        if (hiddenCount > 0) {
            console.log(`隐藏了 ${hiddenCount} 个非目标用户转发`);
        }
    }

    function 删除主页时间线中的推荐(){
        const buttons = document.querySelectorAll('button.css-175oi2r.r-1mmae3n.r-3pj75a.r-1loqt21.r-o7ynqc.r-6416eg.r-1ny4l3l');
        let modifiedCount = 0;

        buttons.forEach(button => {
            if (processedElements.has(button)) return;

            button.innerHTML = '';
            const span = document.createElement('span');
            span.textContent = '不值得关注';
            button.appendChild(span);

            processedElements.add(button);
            modifiedCount++;
        });

        if (modifiedCount > 0) {
            console.log(`修改了 ${modifiedCount} 个推荐按钮`);
        }
    }

    function 删除主页时间线中的引用() {
        const parentDivs = document.querySelectorAll('div.css-175oi2r.r-9aw3ui.r-1s2bzr4');
        let modifiedCount = 0;

        parentDivs.forEach(parentDiv => {
            if (processedElements.has(parentDiv)) return;

            // 直接操作父div的子节点,避免层级过深的选择器
            // 清空所有子节点
            parentDiv.innerHTML = '';

            // 创建新的提示信息
            const newSpan = document.createElement('span');
            newSpan.textContent = '引用已删除';
            newSpan.style.cssText = 'color: #666; font-style: italic; padding: 8px; display: block;';

            parentDiv.appendChild(newSpan);
            modifiedCount++;
            processedElements.add(parentDiv);
        });

        if (modifiedCount > 0) {
            console.log(`修改了 ${modifiedCount} 个引用节点`);
        }
    }
    function 隐藏主页侧边栏你可能会喜欢() {
        const aside = document.querySelector('aside[aria-label="推荐关注"][role="complementary"]');
        if (aside && !processedElements.has(aside)) {
            // 隐藏而不是删除,避免布局抖动和重新渲染
            //aside.style.opacity = '0.5';
            //aside.style.pointerEvents = 'none';

            // 或者完全隐藏
            aside.style.display = 'none';

            processedElements.add(aside);
            console.log('已隐藏主页侧边栏推荐内容');
        }
    }

    function 执行主页清理操作(用户名) {
        console.log('执行主页清理操作');
        隐藏主页侧边栏你可能会喜欢();
        隐藏主页时间线中的转发(用户名);
        删除主页时间线中的推荐();
        删除主页时间线中的引用();
    }

    function 执行主逻辑() {
        if (isExecuting) return;
        isExecuting = true;

        const currentPath = window.location.pathname;
        console.log('执行主逻辑,当前路径:', currentPath);
        let match;


        if (statusPattern.test(currentPath)) {
            console.log('检测到status路径');
            setTimeout(() => {
                隐藏status发现更多之后的推荐;
                isExecuting = false;
            }, 1000);
        } else if ((match = currentPath.match(userPattern))) {
            console.log('检测到主页路径');
            const 用户名 = match[1];
            console.log('用户名:', 用户名);
            setTimeout(() => {
                执行主页清理操作(用户名);
                isExecuting = false;
            }, 1000);
        } else {
            console.log('检测到普通路径,无动作');
            isExecuting = false;
        }
    }

    function 处理URL变化() {
        console.log('URL发生变化,重新执行主逻辑');
        processedElements.clear();
        执行主逻辑();
    }

    function setup监听URL变化() {
        const originalPushState = history.pushState;
        const originalReplaceState = history.replaceState;

        history.pushState = function(...args) {
            originalPushState.apply(this, args);
            setTimeout(处理URL变化, 100);
        };

        history.replaceState = function(...args) {
            originalReplaceState.apply(this, args);
            setTimeout(处理URL变化, 100);
        };

        window.addEventListener('popstate', () => setTimeout(处理URL变化, 100));
        window.addEventListener('hashchange', () => setTimeout(处理URL变化, 100));
    }

    // 新增:滚动检测函数
    function 启动滚动检测() {
        if (scrollCheckInterval) clearInterval(scrollCheckInterval);

        scrollCheckInterval = setInterval(() => {
            const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop;

            // 检测到用户滚动并且滚动距离变化较大
            if (Math.abs(currentScrollTop - lastScrollTop) > 100) {
                lastScrollTop = currentScrollTop;

                const currentPath = window.location.pathname;
                const userPattern = /^\/([^\/?#]+)$/;

                if (userPattern.test(currentPath)) {
                    console.log('检测到滚动,执行清理操作');
                    执行主页清理操作();
                }
            }
        }, 1000); // 每秒检查一次
    }

    function init() {
        setup监听URL变化();
        setTimeout(() => {
            执行主逻辑();
            启动滚动检测(); // 启动滚动检测
        }, 2000);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // 简化MutationObserver,只用于检测主要结构变化
    const observer = new MutationObserver((mutations) => {
        const hasSignificantChanges = mutations.some(mutation => {
            return mutation.addedNodes.length > 0 &&
                   Array.from(mutation.addedNodes).some(node =>
                       node.nodeType === 1 && (
                           node.querySelector('article') ||
                           node.querySelector('[data-testid="tweet"]')
                       )
                   );
        });

        if (hasSignificantChanges) {
            console.log('检测到DOM重大变化,执行清理操作');
            const currentPath = window.location.pathname;
            const userPattern = /^\/([^\/?#]+)$/;

            if (userPattern.test(currentPath)) {
                执行主页清理操作();
            }else if(statusPattern.test(currentPath)){
                隐藏status发现更多之后的推荐();
            }else{console.log(currentPath)}
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();