Greasy Fork is available in English.

TikTok Expand All Comments

Don't want to spam that "View More" button? Expand all comments with one click!

// ==UserScript==
// @name         TikTok Expand All Comments
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Don't want to spam that "View More" button? Expand all comments with one click!
// @author       LL-Studio
// @match        *://*.tiktok.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tiktok.com
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const INTERVAL_TIME = 500;
    const MAX_TIME = 1e4;

    // Styles
    // CSS styling for the "Expand All" button
    var style = document.createElement('style');
    style.innerHTML = `
        .expand-button {
            color: rgba(22, 24, 35, 0.5);
            font-family: TikTokFont, Arial, Tahoma, PingFangSC, sans-serif;
            font-weight: 600;
            font-size: 14px;
            line-height: 18px;
            cursor: pointer;
        }
        .expand-button:hover {
            text-decoration: underline;
        }
    `;
    document.head.appendChild(style);

    // Elements
    // Add custom elements to the webpage
    const waitForElement = (parent, selector) => {
        return new Promise((resolve, reject) => {
            const results = parent.querySelector(selector);
            if (results) return resolve(results);
            const observer = new MutationObserver((mutationsList, observer) => {
                const element = parent.querySelector(selector);
                if (element) {
                    observer.disconnect();
                    resolve(element);
                }
            });
            observer.observe(parent, { childList: true, subtree: true });
        });
    };

    const hydrateActionContainer = (container) => {
        // Create button
        var expandButton = document.createElement("p");
        expandButton.setAttribute("role", "button");
        expandButton.style.color = "rgba(22, 24, 35, 0.5)";
        expandButton.style.fontFamily = "TikTokFont, Arial, Tahoma, PingFangSC, sans-serif";
        expandButton.style.fontWeight = "600";
        expandButton.style.fontSize = "14px";
        expandButton.style.lineHeight = "18px";
        expandButton.style.cursor = "pointer";

        expandButton.innerText = "Expand All";
        expandButton.onclick = () => {
            expandButton.innerText = "Expanding...";
            expandButton.style.pointerEvents = "none";

            let time = 0;
            const id = setInterval(() => {
                const paragraphs = Array.from(container.querySelectorAll('p'));
                const viewMoreButton = paragraphs.find(p => /^View/.test(p.textContent));
                if (viewMoreButton) {
                    time = 0;
                    viewMoreButton.click();
                } else if (time <= MAX_TIME) {
                    time += INTERVAL_TIME;
                } else {
                    clearInterval(id);
                    expandButton.innerText = "Expand All";
                    expandButton.style.pointerEvents = "auto";
                }
            }, INTERVAL_TIME);
        };

        // Update element when container changes
        const updateElement = () => {
            if (expandButton.parentNode) expandButton.parentNode.removeChild(expandButton);
            if (container.childNodes.length > 1) container.insertBefore(expandButton, container.childNodes[1]);
        };

        var observer = new MutationObserver(() => {
            observer.disconnect();
            updateElement();
            observer.observe(container, { childList: true });
        });
        updateElement();
        observer.observe(container, { childList: true });
    }

    waitForElement(document.body, 'div[data-e2e="search-comment-container"]').then((element) => {
        // Observe and add button to all action containers
        const actionContainers = new Set();
        const onContainer = (container) => {
            if (actionContainers.has(container)) return;
            hydrateActionContainer(container);
            actionContainers.add(container);
        };
        const observer = new MutationObserver((mutationsList) => {
            for (const mutation of mutationsList) {
                if (mutation.addedNodes.length) {
                    const container = document.querySelectorAll("div.css-1for4nf-DivReplyActionContainer");
                    container.forEach((container) => onContainer(container));
                }
            }
        });
        element.querySelectorAll("div.css-1for4nf-DivReplyActionContainer").forEach((container) => onContainer(container));
        observer.observe(element, { childList: true, subtree: true });
    });
})();