Greasy Fork is available in English.

Reddit Enhancement

Removes the promotion element and the "We had a server error..." banner

// ==UserScript==
// @name         Reddit Enhancement
// @namespace    http://tampermonkey.net/
// @version      1.0.7
// @description  Removes the promotion element and the "We had a server error..." banner
// @author       aspen138
// @match        *://www.reddit.com/*
// @icon         data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAQlBMVEVHcEz/RQD/RQD/QgD/RQD/RQD/RQD/RQD/RQD/RQD/////MgD/OgD/s5//z8P/a0T/5d3/VyH/iGr/qJP/mYD/+vcCA1U1AAAACnRSTlMAJP//y5WUn+ElsgVe0gAAAJFJREFUGJVtT1sOwyAMy0JpIa/C2t3/qjNQaT+zkMAmD5sIqLkwl1zpwcEPjsW3ScxMefv9m7u3WVNXdXJ9Q+BKGYRN+62miXmnMvg7WotT8SzE6ZQHHzkTL+HuIv2SKRTWkHCRC5eiJWOCSJvnNgzFWrtQ4iGuY+0wZt0jHFuWeVhPpmpwsf0PR/TaR/x9xv8CYoYGnu4Mr1kAAAAASUVORK5CYII=
// @grant        none
// @license      MIT
// ==/UserScript==


//===== hover to show exact time, leave to recover =====
(function() {
    // Function to replace the innerText of <time> elements with their datetime attribute
    function replaceTimeText(node) {
        node.originalText = node.innerText; // Save the original text
        node.innerText = node.attributes.datetime.textContent;
    }

    // Function to recover the original innerText of <time> elements
    function recoverTimeText(node) {
        if (node.originalText !== undefined) {
            node.innerText = node.originalText;
        }
    }

    // Apply the replacement to all existing <time> elements
    const arr = document.evaluate(
        "//time", document, null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
    );

    for (let i = 0; i < arr.snapshotLength; i++) {
        const node = arr.snapshotItem(i);
        node.addEventListener("mouseover", () => replaceTimeText(node));
        node.addEventListener("mouseout", () => recoverTimeText(node));
    }

    // Mutation observer callback to handle dynamically added <time> elements
    function callback(changes, observer) {
        for (let change of changes) {
            if (change.type == "childList") {
                for (let node of change.addedNodes) {
                    if (node.tagName == "TIME") {
                        node.addEventListener("mouseover", () => replaceTimeText(node));
                        node.addEventListener("mouseout", () => recoverTimeText(node));
                    }
                }
            }
        }
    }

    // Observe changes in the DOM
    const body = document.getElementsByTagName("body")[0];
    const config = { childList: true, subtree: true };
    const observer = new MutationObserver(callback);
    observer.observe(body, config);

})();




//===== hide promotion element and banner =====
(function() {
    'use strict';

    // Searches through all Shadow DOM roots
    function deepQuerySelectorAll(selector) {
        const nodes = [];
        function searchInNode(node) {
            if (node.shadowRoot) {
                const matches = node.shadowRoot.querySelectorAll(selector);
                if (matches.length > 0) {
                    nodes.push(...matches);
                }
                Array.from(node.shadowRoot.children).forEach(searchInNode);
            }
            Array.from(node.children).forEach(searchInNode);
        }
        searchInNode(document);
        return nodes;
    }

    // Combined removal function for both error banners and promo elements
    function removeElements() {
        // Remove error banners
        const banners = deepQuerySelectorAll('div.banner.error');
        banners.forEach(banner => {
            banner.remove();
            console.log("Server Error Banner has been removed.");
        });

        // Remove promotional elements
        const promoSelectors = [
            'a.w-100.block.h-100.cursor-pointer',
            'shreddit-ad-post.promotedlink',
            'shreddit-dynamic-ad-link',
            'shreddit-comments-page-ad.promotedlink'
        ];

        promoSelectors.forEach(selector => {
            const promoElements = document.querySelectorAll(selector);
            promoElements.forEach(element => {
                element.remove();
                console.log('Promotion element removed:', selector);
            });
        });

        // Hide elements with specific rel attribute
        const links = document.querySelectorAll('a');
        links.forEach(link => {
            if (link.getAttribute('rel') === 'noopener nofollow sponsored') {
                link.style.display = 'none';
                console.log('Link with rel "noopener nofollow sponsored" hidden');
            }
        });

        // Hide element by data-immersive-translate-walked attribute
        const immersiveElements = deepQuerySelectorAll('div[data-immersive-translate-walked="de111be1-6c63-482a-9f03-7fc8d0ca3ba2"]');
        immersiveElements.forEach(element => {
            element.style.display = 'none';
            console.log('Element hidden with data-immersive-translate-walked="de111be1-6c63-482a-9f03-7fc8d0ca3ba2"');
        });
    }

    // Single MutationObserver for all operations
    const observer = new MutationObserver(() => {
        removeElements();
    });

    // Start observing changes in the document
    observer.observe(document, {
        childList: true,
        subtree: true
    });

    // Perform initial cleanup when the page loads
    if (document.readyState === 'loading') {
        window.addEventListener('load', removeElements);
    } else {
        removeElements();
    }
})();