Greasy Fork is available in English.

隐藏b站视频详情页右侧的"活动推广"和"大家围观的直播"以及首页广告

Hide specified Bilibili elements using MutationObserver

// ==UserScript==
// @name         隐藏b站视频详情页右侧的"活动推广"和"大家围观的直播"以及首页广告
// @name:en      Hide promotions on Bilibili's video details page and homepage
// @namespace    http://tampermonkey.net/
// @version      0.2.0
// @description  Hide specified Bilibili elements using MutationObserver
// @description:en  Hide specified Bilibili elements using MutationObserver
// @author       aspen138
// @match        *://www.bilibili.com/video/*
// @match        *://www.bilibili.com/*
// @match        *://www.bilibili.com
// @match        *://search.bilibili.com/*
// @icon         https://www.bilibili.com/favicon.ico
// @grant        none
// @license      MIT
// ==/UserScript==
 
 
 
(function () {
    'use strict';
 
    if (window.location.hostname.includes('bilibili.com')) {
        const styleElement1 = document.createElement('style');
        styleElement1.textContent = `.login-tip, .vip-login, .vip-login-tip, .login-panel-popover { display: none !important; }`;
        document.head.appendChild(styleElement1);
    }
 
 
    if (document.cookie.includes('DedeUserID')) {
        //console.log("has loged in.");
        // add CSS to hide some elements
        const styleElement = document.createElement('style');
        styleElement.textContent = ` .desktop-download-tip { display: none !important; }`; //隐藏右下角的下载客户端的推广弹窗
        document.head.appendChild(styleElement);
    }
    else {
        //console.log("not loged in.");
        // add CSS to hide some elements
        const originAppendChild = Node.prototype.appendChild;
        Node.prototype.appendChild = function (childElement) {
            if (childElement.tagName === 'SCRIPT' && childElement.src.includes("login")) {
                console.log("src=", src);
                return null;
            }
            else {
                return originAppendChild.call(this, childElement);
            }
        }
    }
 
    // Select elements with href containing 'cm.bilibili.com/cm/api'
    var elements = document.querySelectorAll('a[href*="cm.bilibili.com/cm/api"]');
    elements.forEach(function(element) {
        // Find the closest parent with class 'bili-video-card'
        var parentCard = element.closest('.bili-video-card');
        if (parentCard) {
            // Store original height to maintain layout
            var originalHeight = parentCard.children[0].children[0].offsetHeight;
 
            // Create replacement message div
            var messageDiv = document.createElement('div');
            messageDiv.style.cssText = `
            background-color: #f0f0f0;
            color: #666;
            padding: 15px;
            text-align: center;
            font-size: 14px;
            height: ${originalHeight}px;
            display: flex;
            align-items: center;
            justify-content: center;
        `;
            messageDiv.textContent = "The AD content is hidden";
 
            // Replace content while keeping the card
            parentCard.innerHTML = '';
            parentCard.appendChild(messageDiv);
        }
    });
 
    // Enhanced function to thoroughly hide elements
    function hideElement(element) {
        if (!element) return;
 
        // Apply more aggressive hiding styles
        const hideStyles = {
            'display': 'none !important',
            'visibility': 'hidden !important',
            'opacity': '0 !important',
            'background': 'white !important',
            'color': 'white !important',
            'pointer-events': 'none !important',
            'height': '0 !important',
            'width': '0 !important',
            'overflow': 'hidden !important',
            'position': 'absolute !important',
            'z-index': '-9999 !important',
            'clip': 'rect(0, 0, 0, 0) !important'
        };
 
        // Apply styles using both direct style and cssText for maximum effectiveness
        Object.entries(hideStyles).forEach(([property, value]) => {
            element.style.setProperty(property, value.replace(' !important', ''), 'important');
        });
 
        // Hide all child elements recursively
        Array.from(element.children).forEach(child => {
            hideElement(child);
        });
 
        // Remove any inline event listeners
        element.onclick = null;
        element.onmouseover = null;
        element.onmouseenter = null;
        element.onmouseleave = null;
    }
 
    // Function to handle all target elements
    function hideAllTargetElements() {
        const targetElements = [
            '#slide_ad',
            '#right-bottom-banner',
            '.pop-live-small-mode.part-1',
            '.ad-floor-cover.b-img',
            '#bannerAd',
            '.vcd',
            'a[data-loc-id="4331"]',
            '#activity_vote',
            '.ad-report.video-card-ad-small',
            '.ad-report.ad-floor-exp',
            '.slide-ad-exp',
            '.activity-m-v1.act-now',
            '.video-page-special-card-small',
            '.btn-ad',
            'div[data-v-2ce37bb8].btn-ad',
            '.palette-button-adcard.is-bottom', // New element
            '.palette-button-adcard' // More specific selector for the new element
            //,'div[data-v-7b35db32].vip-login-tip'
        ];
 
        targetElements.forEach(selector => {
            const elements = document.querySelectorAll(selector);
            elements.forEach(hideElement);
        });
    }
 
    // Create a more specific MutationObserver
    const observer = new MutationObserver((mutations) => {
        mutations.forEach(mutation => {
            // Check for added nodes
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) { // Element node
                        // Check if the added node is a target element
                        if (node.id === 'slide_ad' ||
                            node.classList.contains('slide-ad-exp') ||
                            node.classList.contains('ad-report') ||
                            node.classList.contains('activity-m-v1') ||
                            node.classList.contains('video-page-special-card-small') ||
                            node.classList.contains('btn-ad') ||
                            node.classList.contains('palette-button-adcard')) { // Added new class check
                            hideElement(node);
                        }
                        // Also check children of added nodes
                        const targetElements = node.querySelectorAll('#slide_ad, .slide-ad-exp, .ad-report, .activity-m-v1, .video-page-special-card-small, .btn-ad, .palette-button-adcard');
                        targetElements.forEach(hideElement);
                    }
                });
            }
 
            // Check for attribute changes
            if (mutation.type === 'attributes') {
                const element = mutation.target;
                if (element.id === 'slide_ad' ||
                    element.classList.contains('slide-ad-exp') ||
                    element.classList.contains('ad-report') ||
                    element.classList.contains('activity-m-v1') ||
                    element.classList.contains('video-page-special-card-small') ||
                    element.classList.contains('btn-ad') ||
                    element.classList.contains('palette-button-adcard')
                    // ||element.classList.contains('vip-login-tip')
                ) { // Added new class check
                    hideElement(element);
                }
            }
        });
    });
 
    // Configure the observer to watch for everything
    const observerConfig = {
        childList: true,
        subtree: true,
        attributes: true,
        attributeFilter: ['style', 'class']
    };
 
    // Initial hiding
    hideAllTargetElements();
 
    // Start observing
    observer.observe(document.body, observerConfig);
 
    // Set up periodic checks just in case
    const checkInterval = setInterval(hideAllTargetElements, 1000);
 
    // Cleanup after 30 seconds
    setTimeout(() => {
        clearInterval(checkInterval);
        observer.disconnect(); // Optionally disconnect the observer after cleanup
    }, 30000);
})();