Удаление рекламы v2

Удаление элементов по ID, селектору, атрибуту и N-му родителю.

// ==UserScript==
// @name         Удаление рекламы v2
// @namespace    http://tampermonkey.net/
// @version      2025-02-10
// @description  Удаление элементов по ID, селектору, атрибуту и N-му родителю.
// @author       ...
// @match        *://*/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- НАСТРОЙКИ УДАЛЕНИЯ ---

    // 1. Селекторы элементов, которые нужно удалить целиком
    //    Можно использовать ID (#id), классы (.class), атрибуты ([data-attr="value"]) и т.д.
    const selectorsToRemoveSelf = [
        // По ID
        '#animation_container',
        '#n0cyox3',
        '#kvWKB544280753',
        '#AdvProductGalleryLeft__fiw5zyuuoh13y97lat2hs-PPDJVqc',
        '#mTBBFsUcNYzrXnfc',

        // По классам
        '.styles_adBlockWarningRoot__wgxZA',
        '.vwo__cadbbbddaa',
        '.business-card-title-view__advert-unit',
        '.business-card-title-view__call-to-action',
        '.card-special-offers-view._view_narrow',
        '.adfox_shown_yes',
        '.p6g0q6xzgwduth6unylwoifmdwm8kv5l',
        '.afisha-profit-banner',
        '.UZznxc',
        '.adv',
        '.t857fd0ab',
        '.wded4f4e0',
        '.AdvRsya-Slot',
        '.mini-suggest__item_with-button',
        '.main-home-banner',
        '.toponym-card-view__advert-unit',
        '.AdvRsyaCrossPage',
        '.AdvProductGallery',
        '.AppMoneySidebar_wrap__2qJIK',
        '.AppForecastMoney_wrap__KdMw_',
        '.mdAoPtFInYnvQhPtha1k0',
        '.mYgoauUUQQX7LjA',
        '.c__ebfeebccdefea',
        '.aptoj__ffdcdebbbaedddc',
        '.eca1c2c96',
        '.MMSidebar-Card_bottomAdv',

        // По атрибутам
        '[data-tid="37b61dba"]',
        '[data-tid="5da1a3ef"]',
        '[data-tid="90f1e109"]',
        '[data-chunk="promo-chunk"]',
        '[data-test-id="afishaProfit.wrapper"]',
        '[data-log-node="1_m4n9w002-0"]',
    ];

    // 2. Селекторы элементов, у которых нужно удалить N-го родителя
    //    `selector`: CSS-селектор для поиска дочернего элемента
    //    `level`:    Номер родителя для удаления (1 = прямой родитель, 2 = "дедушка" и т.д.)
    const selectorsToRemoveNthParent = [
        // Старые `classesToRemoveParents` (удаляем прямого родителя, level: 1)
        { selector: '.body__feed-wrapper', level: 1 },
        { selector: '.Organic_withAdvLabel', level: 1 },
        { selector: '.business-card-view__offers', level: 1 },
        { selector: '.Organic_withPromoOffer', level: 1 },

        { selector: '.banner-view', level: 3 },

        // Старый `removeAdWrapperElements` (удаляем 4-го родителя, level: 4)
        { selector: '.AdvLabel', level: 3 },
        { selector: '[data-name="adWrapper"]', level: 4 },

        // Пример: Удалить 2-го родителя элемента с классом 'some-ad-container'
        // { selector: '.some-ad-container', level: 2 },

        // Пример: Удалить 3-го родителя элемента с ID 'annoying-popup'
        // { selector: '#annoying-popup', level: 3 },
    ];

    // --- ЛОГИКА УДАЛЕНИЯ ---

    // Удаляет все элементы, соответствующие селектору
    function removeElementsBySelector(selector, context = document) {
        try {
            const elements = context.querySelectorAll(selector);
            elements.forEach(el => {
                el.remove();
                // console.log(`Removed element matching selector: "${selector}"`);
            });
            return elements.length > 0; // Возвращаем true, если что-то удалили
        } catch (error) {
            console.error(`Error removing elements with selector "${selector}":`, error);
            return false;
        }
    }

    // Находит N-го родителя элемента
    function findNthParent(element, level) {
        let parent = element;
        for (let i = 0; i < level; i++) {
            if (!parent.parentElement) {
                return null; // Не удалось подняться на нужный уровень
            }
            parent = parent.parentElement;
        }
        return parent;
    }

    // Удаляет N-го родителя для всех элементов, соответствующих селектору
    function removeNthParentBySelector(selector, level, context = document) {
        if (level < 1) {
            console.warn(`Invalid level ${level} for selector "${selector}". Level must be 1 or greater.`);
            return false;
        }
        let removed = false;
        try {
            const elements = context.querySelectorAll(selector);
            elements.forEach(el => {
                const parentToRemove = findNthParent(el, level);
                if (parentToRemove && parentToRemove !== document.body && parentToRemove !== document.documentElement) {
                    parentToRemove.remove();
                    // console.log(`Removed ${level}-th parent of element matching selector: "${selector}"`);
                    removed = true;
                }
            });
        } catch (error) {
            console.error(`Error removing ${level}-th parent for selector "${selector}":`, error);
        }
        return removed;
    }

    // Главная функция, запускающая все операции удаления
    function runRemovals(context = document) {
        let changesMade = false;

        // 1. Удаляем сами элементы
        selectorsToRemoveSelf.forEach(selector => {
            if (removeElementsBySelector(selector, context)) {
                changesMade = true;
            }
        });

        // 2. Удаляем N-ных родителей
        selectorsToRemoveNthParent.forEach(item => {
            if (removeNthParentBySelector(item.selector, item.level, context)) {
                changesMade = true;
            }
        });

        // Дополнительная очистка (опционально, можно добавить больше правил)
        // Например, удалить пустые контейнеры, оставшиеся после удаления рекламы
        // if (changesMade) {
        //     cleanupEmptyContainers();
        // }
    }

    // --- ИНИЦИАЛИЗАЦИЯ И ОТСЛЕЖИВАНИЕ ---

    // Первичный запуск при загрузке скрипта
    runRemovals();

    // Наблюдатель за изменениями в DOM (для динамически загружаемого контента)
    const observer = new MutationObserver(mutations => {
        // Оптимизация: Проверяем, были ли добавлены узлы перед повторным полным сканированием
        let nodesAdded = false;
        for (const mutation of mutations) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                nodesAdded = true;
                break;
            }
        }

        // Если узлы были добавлены, запускаем проверки снова
        if (nodesAdded) {
            // Можно передать mutation.target или document для сканирования только измененной части,
            // но для простоты и надежности пока сканируем весь документ.
            runRemovals();
        }
    });

    // Начинаем наблюдение за всем документом
    observer.observe(document.body || document.documentElement, {
        childList: true, // Следить за добавлением/удалением дочерних узлов
        subtree: true
    });

    // Опционально: можно отключить наблюдатель через какое-то время, если страница статична
    // setTimeout(() => observer.disconnect(), 30000); // Остановить через 30 сек

})();