Carousell++

Makes Carousell search less annoying and more useful

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Carousell++
// @namespace    http://tampermonkey.net/
// @version      1.1
// @license      GPL-3.0
// @description  Makes Carousell search less annoying and more useful
// @author       oopsitsdeleted
// @match        https://www.carousell.sg/search/*
// @match        https://www.carousell.com.my/search/*
// @match        https://www.carousell.ph/search/*
// @match        https://www.carousell.com.hk/search/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // Log removal events only if you want to debug.
    const DEBUG = true;

    /**
     * removes all the annoying bs from carousell search (boosted/sponsored) hehe 99.999% vibecoded
     */
    function removeSponsoredPosts() {
        let removedCount = 0;

        // --- boosted/spotlight ads ---

        // find boosted
        const boostedIndicators = document.querySelectorAll('svg[fill="#00816D"]');

        // find spotlight indicators
        const spotlightIndicators = document.querySelectorAll('div[style*="background: rgb(169, 80, 168)"]');

        // Combine all listing indicators into one NodeList
        const allListingIndicators = [...boostedIndicators, ...spotlightIndicators];

        allListingIndicators.forEach(indicatorElement => {
            let cardElement = indicatorElement;
            let depth = 0;
            const MAX_DEPTH = 10;

            // main card wrapper
            while (cardElement && depth < MAX_DEPTH) {
                // rabs
                if (cardElement.hasAttribute('data-testid') && cardElement.getAttribute('data-testid').startsWith('listing-card')) {

                    // remove parents
                    const removalTarget = cardElement.parentElement;

                    if (removalTarget && removalTarget.parentElement) {
                        if (DEBUG) console.log(`[Carousell++] Removing Boosted/Spotlight Listing:`, removalTarget);
                        removalTarget.parentElement.removeChild(removalTarget);
                        removedCount++;
                        return;
                    }
                }
                cardElement = cardElement.parentElement;
                depth++;
            }
        });

        // --- banner ads ---

        // target shoutout banner
        const shoutoutSelector = '.D_bNr';
        const shoutoutBanners = document.querySelectorAll(shoutoutSelector);

        shoutoutBanners.forEach(banner => {
            // remove grid item and collapse space so it doesnt leave blank space
            // Shoutout Banner (.D_bNr) -> Parent DIV (1st level) -> Grid Item Container (2nd level, what we want to remove)
            const parent = banner.parentElement;
            const removalTarget = parent ? parent.parentElement : null;

            if (removalTarget && removalTarget.parentElement) {
                if (DEBUG) console.log(`[Carousell++] Removing Shoutout Banner Grid Container:`, removalTarget);
                removalTarget.parentElement.removeChild(removalTarget);
                removedCount++;
            }
        });

        // --- big ass carousell ads ---

        // target banner
        const smartRenderSelector = '.D_aEW';
        const smartRenderBanners = document.querySelectorAll(smartRenderSelector);

        smartRenderBanners.forEach(banner => {
            // remove grid item
            // .D_aEW -> Parent DIV (.D_ZP) -> Grid Item Container (what we want to remove)
            const removalTarget = banner.parentElement;

            if (removalTarget && removalTarget.parentElement) {
                if (DEBUG) console.log(`[Carousell++] Removing Smart Render/Carousell Ad Grid Container:`, removalTarget);
                removalTarget.parentElement.removeChild(removalTarget);
                removedCount++;
            }
        });

        // --- big big big banner ---

        // target banner
        const promotedContentSelector = '.D_ais';
        const promotedContentBanners = document.querySelectorAll(promotedContentSelector);

        promotedContentBanners.forEach(banner => {
            // hmmm
            const removalTarget = banner;

            if (removalTarget && removalTarget.parentElement) {
                if (DEBUG) console.log(`[Carousell++] Removing Promoted Content Banner:`, removalTarget);
                removalTarget.parentElement.removeChild(removalTarget);
                removedCount++;
            }
        });


        if (DEBUG && removedCount > 0) {
            console.log(`[Carousell++] Successfully removed ${removedCount} annoying bs.`);
        }
    }

    /**
     * Use MutationObserver to watch for new content being loaded (e.g., infinite scroll)
     * and run the removal function again.
     */
    function observeDOM() {
        // Observe the main content area (document.body) for new elements.
        const targetNode = document.body;

        const config = { childList: true, subtree: true };

        const observer = new MutationObserver((mutationsList, observer) => {
            // Run the removal logic every time the DOM changes.
            removeSponsoredPosts();
        });

        // Start observing the target node for configured mutations
        observer.observe(targetNode, config);

        // Run the function immediately on page load, as the observer might miss initial content
        removeSponsoredPosts();
    }

    // Start the whole process
    observeDOM();

})();