网页元素屏蔽器 (长按屏蔽)

在手机上长按一个元素,会弹出一个菜单来屏蔽它。可以记住你的选择。

当前为 2025-08-28 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Website Element Blocker (Long-press to block)
// @name:zh-CN  网页元素屏蔽器 (长按屏蔽)
// @namespace   http://tampermonkey.net/
// @version     1.5
// @description Long-press on an element on your phone to bring up a menu to block it. Remembers your choices.
// @description:zh-CN 在手机上长按一个元素,会弹出一个菜单来屏蔽它。可以记住你的选择。
// @author      Your AI Assistant
// @match       *://*/*
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_registerMenuCommand
// @grant       GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // --- 配置 ---
    const LONG_PRESS_DURATION = 500; // 长按时长 (毫秒)
    const STORAGE_KEY_PREFIX = 'element_blocker_';

    let pressTimer = null;
    let longPressFired = false;
    let targetElement = null;
    let animationIntervalId = null; // 用于存储 setInterval 的 ID

    // --- 样式注入 ---
    GM_addStyle(`
        .blocker-highlight {
            position: relative !important;
            z-index: 2147483646 !important;
        }
        
        .blocker-highlight::before {
            content: '';
            position: absolute;
            top: -4px;
            left: -4px;
            right: -4px;
            bottom: -4px;
            border-radius: 4px;
            z-index: 2147483645;
            animation: blocker-rainbow-border 2s linear infinite;
            pointer-events: none;
        }
        
        @keyframes blocker-rainbow-border {
            0% { box-shadow: 0 0 0 4px rgba(255, 0, 0, 0.8); }
            16% { box-shadow: 0 0 0 4px rgba(255, 127, 0, 0.8); }
            33% { box-shadow: 0 0 0 4px rgba(255, 255, 0, 0.8); }
            50% { box-shadow: 0 0 0 4px rgba(0, 255, 0, 0.8); }
            66% { box-shadow: 0 0 0 4px rgba(0, 0, 255, 0.8); }
            83% { box-shadow: 0 0 0 4px rgba(75, 0, 130, 0.8); }
            100% { box-shadow: 0 0 0 4px rgba(143, 0, 255, 0.8); }
        }

        #blocker-menu {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: #333;
            color: white;
            padding: 10px;
            border-radius: 8px;
            z-index: 2147483647 !important;
            display: flex;
            flex-direction: column;
            gap: 8px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.4);
            font-family: sans-serif;
            font-size: 16px;
        }
        #blocker-menu button {
            background-color: #555;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 5px;
            cursor: pointer;
            text-align: center;
        }
        #blocker-menu button:hover {
            background-color: #777;
        }
    `);

    // --- 核心功能 ---
    function getCssSelector(el) {
        if (!(el instanceof Element)) return;
        const path = [];
        while (el.nodeType === Node.ELEMENT_NODE) {
            let selector = el.nodeName.toLowerCase();
            if (el.id) {
                selector += `#${el.id}`;
                path.unshift(selector);
                break;
            } else {
                let sib = el, nth = 1;
                while (sib.previousElementSibling) {
                    sib = sib.previousElementSibling;
                    if (sib.nodeName.toLowerCase() == selector) nth++;
                }
                if (nth != 1) selector += `:nth-of-type(${nth})`;
            }
            path.unshift(selector);
            el = el.parentNode;
        }
        return path.join(' > ');
    }

    function showBlockMenu(el) {
        // 移除旧菜单
        const oldMenu = document.getElementById('blocker-menu');
        if (oldMenu) oldMenu.remove();

        const menu = document.createElement('div');
        menu.id = 'blocker-menu';

        let currentEl = el;
        let level = 0;

        const createButton = (elem, text) => {
            const button = document.createElement('button');
            button.textContent = text;
            button.onclick = (e) => {
                e.stopPropagation();
                blockElement(elem);
                hideBlockMenu();
            };
            button.onmouseover = () => highlightElement(elem);
            button.onmouseout = () => highlightElement(targetElement);
            return button;
        };

        // 添加 "屏蔽当前" 按钮
        menu.appendChild(createButton(currentEl, `屏蔽当前 (${currentEl.tagName.toLowerCase()})`));

        // 添加 "屏蔽上层" 按钮 (最多5层)
        while (currentEl.parentElement && level < 5) {
            currentEl = currentEl.parentElement;
            if (currentEl.tagName.toLowerCase() === 'body' || currentEl.tagName.toLowerCase() === 'html') break;
            level++;
            menu.appendChild(createButton(currentEl, `屏蔽上${level}层 (${currentEl.tagName.toLowerCase()})`));
        }

        const cancelButton = document.createElement('button');
        cancelButton.textContent = '取消';
        cancelButton.style.backgroundColor = '#800';
        cancelButton.onclick = hideBlockMenu;
        menu.appendChild(cancelButton);

        document.body.appendChild(menu);
        highlightElement(el);
    }

    function hideBlockMenu() {
        const menu = document.getElementById('blocker-menu');
        if (menu) menu.remove();
        unhighlightAll();
    }

    function highlightElement(el) {
        unhighlightAll();
        if(el) el.classList.add('blocker-highlight');
    }

    function unhighlightElement(el) {
        if(el) el.classList.remove('blocker-highlight');
    }

    function unhighlightAll() {
        document.querySelectorAll('.blocker-highlight').forEach(e => e.classList.remove('blocker-highlight'));
    }

    function blockElement(el) {
        const selector = getCssSelector(el);
        if (!selector) return;

        const hostname = window.location.hostname;
        const key = STORAGE_KEY_PREFIX + hostname;
        const blockedSelectors = GM_getValue(key, []);

        if (!blockedSelectors.includes(selector)) {
            blockedSelectors.push(selector);
            GM_setValue(key, blockedSelectors);
            applyBlocking(hostname);
            console.log(`[Element Blocker] 已屏蔽: ${selector}`);
        }
    }

    function applyBlocking(hostname) {
        const key = STORAGE_KEY_PREFIX + hostname;
        const selectors = GM_getValue(key, []);
        if (selectors.length > 0) {
            let style = document.getElementById('dynamic-blocker-style');
            if (!style) {
                style = document.createElement('style');
                style.id = 'dynamic-blocker-style';
                document.head.appendChild(style);
            }
            style.textContent = `${selectors.join(', ')} { display: none !important; }`;
            console.log(`[Element Blocker] 已应用 ${selectors.length} 条屏蔽规则于 ${hostname}`);
        }
    }

    function clearBlockingRules() {
        const hostname = window.location.hostname;
        const key = STORAGE_KEY_PREFIX + hostname;
        GM_setValue(key, []);
        const style = document.getElementById('dynamic-blocker-style');
        if (style) style.textContent = '';
        alert(`已清除网站 [${hostname}] 的所有屏蔽规则。`);
    }

    // --- 事件监听 ---
    function onTouchStart(e) {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) {
            return;
        }

        longPressFired = false;
        targetElement = e.target;
        pressTimer = window.setTimeout(() => {
            longPressFired = true;
            e.preventDefault();
            showBlockMenu(targetElement);
        }, LONG_PRESS_DURATION);
    }

    function onTouchEnd(e) {
        clearTimeout(pressTimer);
    }

    function onTouchMove(e) {
        clearTimeout(pressTimer);
    }

    // 绑定事件
    window.addEventListener('touchstart', onTouchStart, { passive: false });
    window.addEventListener('touchend', onTouchEnd);
    window.addEventListener('touchmove', onTouchMove);

    // --- 页面加载时应用规则 & 注册菜单 ---
    applyBlocking(window.location.hostname);
    GM_registerMenuCommand('清除当前网站的屏蔽规则', clearBlockingRules);
})();