长按删除网页元素(v1.4 修复菜单闪退)

安卓浏览器长按弹菜单:删除/取消,带高亮,点击外部关闭,修复菜单闪一下就消失的问题。

// ==UserScript==
// @name         长按删除网页元素(v1.4 修复菜单闪退)
// @namespace    custom-longpress-delete
// @version      1.4
// @description  安卓浏览器长按弹菜单:删除/取消,带高亮,点击外部关闭,修复菜单闪一下就消失的问题。
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const LONGPRESS_DELAY = 600;
    const MAX_Z = 2147483647;

    let longPressTimer = null;
    let pressedElement = null;
    let originalStyles = new WeakMap();
    let menuVisible = false;
    let touchActive = false;

    // ===== 菜单 =====
    const menu = document.createElement('div');
    Object.assign(menu.style, {
        position: 'fixed',
        display: 'none',
        padding: '10px 14px',
        borderRadius: '10px',
        background: 'rgba(0,0,0,0.88)',
        color: '#fff',
        fontSize: '16px',
        zIndex: String(MAX_Z),
        userSelect: 'none',
        boxShadow: '0 6px 18px rgba(0,0,0,0.35)',
        minWidth: '130px',
        textAlign: 'center',
        transform: 'translate(-50%, -120%)'
    });

    const delBtn = document.createElement('div');
    delBtn.innerText = '删除元素';
    delBtn.style.padding = '8px 0';
    delBtn.style.cursor = 'pointer';
    delBtn.onclick = e => {
        e.stopPropagation();
        if (pressedElement) pressedElement.remove();
        hideMenu();
    };

    const cancelBtn = document.createElement('div');
    cancelBtn.innerText = '取消';
    cancelBtn.style.padding = '8px 0';
    cancelBtn.style.cursor = 'pointer';
    cancelBtn.style.marginTop = '6px';
    cancelBtn.style.borderTop = '1px solid rgba(255,255,255,0.15)';
    cancelBtn.onclick = e => {
        e.stopPropagation();
        hideMenu();
    };

    menu.appendChild(delBtn);
    menu.appendChild(cancelBtn);
    document.body.appendChild(menu);

    // ===== 显示 / 隐藏 =====
    function showMenuAt(x, y) {
        menu.style.left = x + 'px';
        menu.style.top = y + 'px';
        menu.style.display = 'block';
        menuVisible = true;
    }

    function hideMenu() {
        if (pressedElement && originalStyles.has(pressedElement)) {
            const orig = originalStyles.get(pressedElement);
            pressedElement.style.outline = orig.outline || '';
            pressedElement.style.outlineOffset = orig.outlineOffset || '';
            originalStyles.delete(pressedElement);
        }
        pressedElement = null;
        menu.style.display = 'none';
        menuVisible = false;
    }

    // ===== 事件绑定 =====
    document.addEventListener('touchstart', e => {
        if (e.target.closest('#longpress-menu')) return;
        touchActive = true;
        pressedElement = e.target;

        const touch = e.touches[0];
        const x = touch.clientX;
        const y = touch.clientY;

        longPressTimer = setTimeout(() => {
            if (!pressedElement) return;

            // 保存并高亮
            originalStyles.set(pressedElement, {
                outline: pressedElement.style.outline || '',
                outlineOffset: pressedElement.style.outlineOffset || ''
            });
            pressedElement.style.outline = '3px solid rgba(255,0,0,0.6)';
            pressedElement.style.outlineOffset = '-3px';

            showMenuAt(x, y);
            e.preventDefault(); // 阻止原生长按菜单
        }, LONGPRESS_DELAY);
    }, { passive: false });

    document.addEventListener('touchmove', e => {
        // 移动时只取消未触发的长按
        if (longPressTimer) {
            clearTimeout(longPressTimer);
            longPressTimer = null;
        }
    }, { passive: true });

    document.addEventListener('touchend', e => {
        touchActive = false;
        clearTimeout(longPressTimer);
        longPressTimer = null;

        if (menuVisible) {
            // 如果点击在菜单外部,就关闭
            if (!e.target.closest('#longpress-menu')) {
                hideMenu();
            }
        } else {
            pressedElement = null;
        }
    }, { passive: true });

    document.addEventListener('touchcancel', () => {
        touchActive = false;
        clearTimeout(longPressTimer);
        longPressTimer = null;
        hideMenu();
    });

    // 滚动时收起菜单
    window.addEventListener('scroll', () => {
        if (menuVisible) hideMenu();
    }, { passive: true });

    // 拦截 contextmenu
    document.addEventListener('contextmenu', e => {
        if (menuVisible) e.preventDefault();
    }, true);
})();