Website Element Blocker (Long-press to block)

Long-press on an element on your phone to bring up a menu to block it. Remembers your choices.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Website Element Blocker (Long-press to block)
// @name:zh-CN  网页元素屏蔽器 (长按屏蔽)
// @namespace   http://tampermonkey.net/
// @version     1.2
// @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;

    // --- 样式注入 ---
    GM_addStyle(`
        .blocker-highlight {
            outline: 2px dashed red !important;
            box-shadow: 0 0 10px 5px rgba(255, 0, 0, 0.5) !important;
            background-color: rgba(255, 0, 0, 0.2) !important;
            z-index: 999999 !important;
        }
        #blocker-menu {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: #333;
            color: white;
            padding: 10px;
            border-radius: 8px;
            z-index: 1000000;
            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 = () => unhighlightElement(elem);
            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);
            }
            // 使用 display: none !important; 来确保隐藏
            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);
})();