删除节点

remove any dom you hate

// ==UserScript==
// @name         删除节点
// @license      MIT
// @version      1.0.3
// @namespace    http://tampermonkey.net/
// @description  remove any dom you hate
// @author       IBAS
// @match        *://*/*
// @icon         https://i0.hdslb.com/bfs/face/e86287c6372e8c603724f65049cad9671fa1460c.jpg@240w_240h_1c_1s_!web-avatar-nav.avif
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    const domAction = {
        delete: "delete",
        hide: "hide",
        nothing: "nothing",
    };
    const selectorType = {
        all: "all",
        single: "single",
    };
    const formatSelector = function (selector, action, selectorType) {
        if (typeof selector === "string") {
            return {
                selector: selector,
                action: domAction.delete,
                selectorType: selectorType,
                msg: "",
            }
        } else {
            return selector;
        }
    };
    let updateLocalStorage = (function () {
        const cache_name = "test_rm_cache_keep_090812";
        let cache = [];
        let init = false;
        const getIndex = function (selector, append = false) {
            let i = 0;
            for (;i < cache.length; i++) {
                if (cache[i].selector === selector) {
                    break;
                }
            }
            if (i === cache.length) {
                if (append) {
                    cache.push(formatSelector(selector));
                    return i;
                } else {
                    return -1;
                }
            }
            return i;
        }
        return {
            init() {
                if (init) {
                    return cache;
                } else {
                    const cacheContent = localStorage.getItem(cache_name);
                    if (cacheContent) {
                        try {
                            cache = JSON.parse(cacheContent);
                        } catch (e) {
                            cache = [];
                        }
                        finally {
                        }
                    } else {
                        cache = [];
                    }
                    init = true;
                    return cache;
                }
            },
            set(selector, action = domAction.delete, sType = selectorType.single, msg = "") {
                let i = getIndex(selector, true);
                if (action && action in domAction) {
                    cache[i].action = action;
                }
                if (sType && sType in selectorType) {
                    cache[i].selectorType = sType;
                }
                if (msg) {
                    cache[i].msg = msg;
                }
                localStorage.setItem(cache_name, JSON.stringify(cache));
            },
            remove(selector) {
                let i = getIndex(selector, false);
                if (i !== -1) {
                    cache.splice(i, 1);
                    localStorage.setItem(cache_name, JSON.stringify(cache));
                }
            }
        }
    })();
    function getSelector(element) {
        if (!element) return '';

        // 如果元素有 id,直接返回 id 选择器
        if (element.id) {
            return `#${element.id}`;
        }

        // 获取标签名
        let selector = element.tagName.toLowerCase();
        if (selector === 'body') {
            return selector;
        }

        // 如果有类名,添加类选择器
        if (element.className) {
            const classNames = element.className.trim().split(/\s+/);
            // if (classNames.includes(labelCssName)) {
            //     classNames.splice(classNames.indexOf(labelCssName), 1);
            // }
            selector += '.' + classNames.join('.');
        }

        // 如果是兄弟节点中的唯一一个同类节点,返回标签名和类名
        let sibling = element;
        let siblingIndex = 0;
        while (sibling.previousElementSibling) {
            sibling = sibling.previousElementSibling;
            siblingIndex++;
        }

        if (siblingIndex > 0) {
            selector += `:nth-child(${siblingIndex + 1})`;
        }

        // 递归获取父元素的选择器,并加上当前选择器
        const parent = element.parentElement;
        if (parent) {
            return `${getSelector(parent)} > ${selector}`;
        } else {
            return selector;
        }
    };
    let setStyle = function (style) {
        const styleDom = document.createElement("style");
        styleDom.innerText = style;
        document.head.append(styleDom);
    };
    let labelCssName = "";
    const deleteElement = function (target) {
        target.classList.remove(labelCssName);
        let selector = getSelector(target);
        const msg = prompt("标签", "x");
        if (target.id) {
            updateLocalStorage.set(selector, "", "", msg);
            // 去除 id,有些网站的 id 是随机的,如果不删除,可能下次将失效
            target.id = "";
            selector = getSelector(target);
        }
        updateLocalStorage.set(selector, "", "", msg);
        target.remove();
    }
    let createChildNode = (function () {
        let historyList = [];
        const info = {
            action: '',
            target: null,
            dom: null,
        };
        const render = function () {
            historyList.forEach(h => {
                if (h.classList) {
                    h.classList.remove(labelCssName)
                }
            });
            historyList = [];
            if (info.target) {
                historyList.push(info.target);
                info.target.classList.add(labelCssName);
            }
        };
        const doAction = function () {
            switch (info.action) {
                case "up":
                    if (info.target === document.body) {
                        alert("无法继续到上一级了");
                    } else {
                        info.target = info.target.parentNode;
                    }
                    break;
                case "down":
                    const child = info.target.children;
                    if (child && child.length) {
                        info.target = child[0];
                    }
                    break;
                case "left":
                    if (info.target.previousElementSibling) {
                        info.target = info.target.previousElementSibling;
                    }
                    break;
                case "right":
                    if (info.target.nextElementSibling) {
                        info.target = info.target.nextElementSibling;
                    }
                    break;
                case "delete":
                    info.dom.style.display = "none";
                    deleteElement(info.target);
                case "close":
                    info.target = null;
                    info.dom.style.display = "none";
                    break;
            }
            render();
        };
        function first(dom) {
            let id = dom.id;
            labelCssName = `${id}_box_line_item_label`;
            setStyle(`
    #${id}_box {
        display: flex;
        flex-direction: column;
        gap: 2px;
    }
    #${id}_box_line {
        display: flex;
        flex-direction: row;
        gap: 2px;
    }
    .${id}_box_line_item {
        flex: 1;
        background: #03A9F4;
        width: 30px;
        text-align: center;
    }
    `);
            setStyle(`
    .${labelCssName} {
        background: repeating-linear-gradient(45deg, #fff, #fff 10px, #66A1FF 10px, #66a1ff 20px);
        border: 8px solid red;
    }
    `);
            dom.innerHTML = `<div id="${id}_box">
    <div id="${id}_box_line">
        <div class="${id}_box_line_item"></div>
        <div class="${id}_box_line_item" action="up">上</div>
        <div class="${id}_box_line_item" action="close">关</div>
    </div>
    <div id="${id}_box_line">
        <div class="${id}_box_line_item" action="left">左</div>
        <div class="${id}_box_line_item" action="delete">删</div>
        <div class="${id}_box_line_item" action="right">右</div>
    </div>
    <div id="${id}_box_line">
        <div class="${id}_box_line_item"></div>
        <div class="${id}_box_line_item" action="down">下</div>
        <div class="${id}_box_line_item"></div>
    </div>
</div>`;
            new Array(...dom.getElementsByClassName(`${id}_box_line_item`)).forEach(ele => {
                ele.onclick = function () {
                    const action = this.getAttribute("action");
                    info.action = action;
                    if (action) {
                        doAction();
                    }
                };
            });
            first = () => {};
        };
        return function (dom, target) {
            dom.style.display = "block";
            first(dom);
            info.dom = dom;
            info.target = target;
            render();
        };
    })();

    let getDom = (function() {
        const id = "menu_id_ibas_test_rm";
        let ele = null;
        setStyle(`
        #menu_id_ibas_test_rm {
            display: block;
            position: fixed;
            z-index: 10000000000000000;
            background: darkgray;
            padding: 5px;
            border-radius: 5px;
            width: 100px;
            text-align: center;
            cursor: pointer;
            border: 2px solid gray;
            border-radius: 4px;
        }`);
        function createDom() {
            ele = document.createElement("div");
            ele.id = id;
            document.body.append(ele);
            return ele;
        };

        return function() {
            return ele ? ele : createDom();
        };
    })();
    const table = (function () {
        const tbodyId = `menu_id_ibas_test_rm_panel_table_tbody`;
        let div = null;
        setStyle(`
    #menu_id_ibas_test_rm_panel {
        z-index: 1000000000000000000;
        position: fixed;
        top: 0px;
        left: 0px;
        width: 100vw;
        height: 100vh;
        background: #2196f340;
        cursor: pointer;
    }
    #menu_id_ibas_test_rm_panel_content {
        width: calc(100% - 60px);
        height: calc(100% - 60px);
        border-radius: 30px;
        margin: 30px;
        background: bisque;
        padding: 20px;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        height: calc(100vh - 60px);
    }
    .menu_id_ibas_test_rm_panel_content_table_dom {
        flex: 1;
        overflow-y: scroll;
    }
    .menu_id_ibas_test_rm_panel_title {
        height: 30px;
        line-height: 30px;
    }
    .menu_id_ibas_test_rm_panel_title_btn {
        line-height: 30px;
        text-decoration: underline;
        cursor: pointer;
        margin: 0 10px 0 0;
    }
    .menu_id_ibas_test_rm_panel_content_table {
        width: 100%;
        border-collapse: collapse;
        font-family: Arial, sans-serif;
        table-layout: fixed; /* 让表格列可以自适应 */
    }
    .menu_id_ibas_test_rm_panel_content_table th, .menu_id_ibas_test_rm_panel_content_table td {
        padding: 12px;
        border: 1px solid #ddd;
        text-align: left;
    }
    .menu_id_ibas_test_rm_panel_content_table th {
        background-color: #f2f2f2;
        font-weight: bold;
    }
    .menu_id_ibas_test_rm_panel_content_table tr:nth-child(even) {
        background-color: #f9f9f9;
    }
    .menu_id_ibas_test_rm_panel_content_table tr:hover {
        background-color: #f1f1f1;
    }
    .menu_id_ibas_test_rm_panel_content_table th:nth-child(-n+5), .menu_id_ibas_test_rm_panel_content_table td:nth-child(-n+5) {
        width: 60px; /* 第一列宽度固定为150px */
    }`);
        function init() {
            div = document.createElement('div');
            div.style.display = 'none';
            div.id = `menu_id_ibas_test_rm_panel`;
            div.innerHTML = `<div id="menu_id_ibas_test_rm_panel_content">
        <div class="menu_id_ibas_test_rm_panel_title">
            <span class="menu_id_ibas_test_rm_panel_title_close menu_id_ibas_test_rm_panel_title_btn">关闭</span>
            <span class="menu_id_ibas_test_rm_panel_title_add menu_id_ibas_test_rm_panel_title_btn">添加</span>
        </div>
        <div class="menu_id_ibas_test_rm_panel_content_table_dom">
            <table class="menu_id_ibas_test_rm_panel_content_table">
                <thead>
                <tr>
                    <th>删除</th>
                    <th>隐藏</th>
                    <th>跳过</th>
                    <th>qAll</th>
                    <th>删除</th>
                    <th>标签</th>
                    <th>选择器</th>
                </tr>
                </thead>
                <tbody id="${tbodyId}">
                </tbody>
            </table>
        </div>
    </div>`;
            document.body.append(div);
            document.getElementsByClassName('menu_id_ibas_test_rm_panel_title_close')[0].onclick = function () {
                div.style.display = 'none';
            };
            document.getElementsByClassName('menu_id_ibas_test_rm_panel_title_add')[0].onclick = function () {
                alert("开发中... ...")
            };
            init = () => {};
        }
        return {
            showTable() {
                init();
                let tbody = document.getElementById(tbodyId);
                tbody.innerHTML = '';
                let newDom = ``;
                updateLocalStorage.init().forEach((cache, ind) => {
                    const id = `table_label_${(Math.random() * 1000).toFixed()}_${ind}`
                    cache = formatSelector(cache);
                    newDom += `<tr class="${id}">
                <td><input type="radio" action="${domAction.delete}" name="${cache.selector}" ${cache.action === domAction.delete ? 'checked' : ''}/></td>
                <td><input type="radio" action="${domAction.hide}" name="${cache.selector}" ${cache.action === domAction.hide ? 'checked' : ''}/></td>
                <td><input type="radio" action="${domAction.nothing}" name="${cache.selector}" ${cache.action === domAction.nothing ? 'checked' : ''}/></td>
                <td><input type="checkbox" ${cache.selectorType === selectorType.all ? 'checked' : ''} name="${cache.selector}" label="${id}"/><span id="${id}">${cache.selectorType === selectorType.all ? "多" : "单"}</span></td>
                <td><button name="${cache.selector}" label="${id}">删</button></td>
                <td>${cache.msg}</td>
                <td>${cache.selector}</td>
            </tr>`;
                });
                tbody.innerHTML = newDom;
                new Array(...tbody.getElementsByTagName('input')).forEach(inp => {
                    inp.onchange = function () {
                        if (inp.type === 'radio') {
                            const selector = this.getAttribute('name');
                            const action = this.getAttribute('action');
                            updateLocalStorage.set(selector, action);
                        } else if (inp.type === 'checkbox') {
                            const selector = this.getAttribute('name');
                            const label = this.getAttribute('label');
                            const checked = this.checked;
                            updateLocalStorage.set(selector, "", checked ? selectorType.all : selectorType.single);
                            document.getElementById(label).innerText = checked ? "多" : "单";
                        }
                    };
                });
                new Array(...tbody.getElementsByTagName('button')).forEach(btn => {
                    btn.onclick = function () {
                        const selector = this.getAttribute('name');
                        while (true) {
                            const ret = prompt("确定删除?(yes/y 或 no/n)", "yes");
                            if (ret.startsWith('y') || ret.startsWith('Y')) {
                                updateLocalStorage.remove(selector);
                                const label = this.getAttribute('label');
                                const tr = document.getElementsByClassName(label);
                                if (tr && tr.length) {
                                    tr[0].remove();
                                }
                                break;
                            } else if (ret.startsWith('n') || ret.startsWith('N')) {
                                break;
                            }
                        }
                    };
                });
                div.style.display = 'block';
            },
        };
    })();

    let windowOnLoad = function () {
        const cache = new Array(...updateLocalStorage.init());
        const removeDom = function () {
            const undo = [];
            while (cache.length) {
                const c = formatSelector(cache.shift());
                let dom = [];
                if (c.selectorType === selectorType.all) {
                    dom = document.querySelectorAll(c.selector);
                } else {
                    dom = [document.querySelector(c.selector)];
                }
                if (dom.length) {
                    if (c.action === domAction.delete) {
                        dom.forEach(d => d ? d.remove() : null);
                        // dom.remove();
                    } else if (c.action === domAction.hide) {
                        dom.forEach(d => d ? (d.style.display = 'none') : null);
                        // dom.style.display = 'none';
                    } else {
                        // nothing
                    }
                } else {
                    undo.push(c);
                    return;
                }
            }
            cache.splice(0,0, ...undo);
        };
        let run = false;
        let time = 10;
        let id = setInterval(() => {
            if (run) {} else {
                if (!time) {
                    clearInterval(id);
                    console.log("delete over");
                    return;
                }
                time--;
                run = true;
                removeDom();
                run = false;
                if (!cache.length) {
                    clearInterval(id);
                    console.log("delete over");
                }
            }
        }, 500);


        document.addEventListener('keydown', function(event) {
            // 检查是否按下 Ctrl + S
            if (event.ctrlKey && event.key === 'q') {
                // 在这里编写保存逻辑
                table.showTable();
            }
        });
    };

    document.addEventListener('contextmenu', function(event) {
        console.log(event);
        event.preventDefault(); // 阻止默认右键菜单

        const menu = getDom();
        createChildNode(menu, event.target);
        menu.style.top = event.clientY + 'px';
        menu.style.left = event.clientX + 'px';
        menu.style.display = 'block';
    });
    windowOnLoad();
})();