X.com Timeline Auto-Load with Uninterrupted Reading

Automatically loads new posts on X.com while keeping the reading position intact. Sets a virtual marker at the last visible handler (e.g., @username) before loading new posts and restores the view to this marker.

目前為 2024-11-21 提交的版本,檢視 最新版本

// ==UserScript==
// @name               X.com Timeline Auto-Load with Uninterrupted Reading
// @name:de            X.com Timeline Auto-Load mit unterbrechungsfreiem Lesen
// @name:fr            X.com Timeline Auto-Load avec lecture ininterrompue
// @name:es            Carga automática de la línea de tiempo de X.com con lectura sin interrupciones
// @name:it            Caricamento automatico della timeline di X.com con lettura ininterrotta
// @name:zh            X.com 时间线自动加载,无缝阅读
// @name:ja            X.com タイムライン自動読み込みと中断のない読書
// @description        Automatically loads new posts on X.com while keeping the reading position intact. Sets a virtual marker at the last visible handler (e.g., @username) before loading new posts and restores the view to this marker.
// @description:de     Lädt automatisch neue Beiträge auf X.com, ohne die Leseposition zu verlieren. Setzt eine virtuelle Markierung am letzten sichtbaren Handler (z. B. @Benutzername) vor dem Laden neuer Beiträge und stellt die Ansicht zu dieser Markierung wieder her.
// @description:fr     Charge automatiquement les nouveaux messages sur X.com tout en conservant la position de lecture. Place un marqueur virtuel au dernier handle visible (par exemple, @nomutilisateur) avant de charger les nouveaux messages et restaure la vue à ce marqueur.
// @description:es     Carga automáticamente nuevos posts en X.com mientras mantiene la posición de lectura intacta. Coloca un marcador virtual en el último manejador visible (por ejemplo, @nombredeusuario) antes de cargar nuevos posts y restaura la vista a ese marcador.
// @description:it     Carica automaticamente nuovi post su X.com mantenendo intatta la posizione di lettura. Imposta un segnalibro virtuale sull'ultimo handle visibile (es. @nomeutente) prima di caricare nuovi post e ripristina la vista su quel segnalibro.
// @description:zh     在X.com上自动加载新帖子,同时保持阅读位置不变。在加载新帖子之前,在最后一个可见的处理器(例如@用户名)处设置一个虚拟标记,并将视图恢复到该标记。
// @description:ja     X.comで新しい投稿を自動的に読み込み、読書位置をそのまま保持します。新しい投稿を読み込む前に、最後に見えるハンドル(例:@ユーザー名)に仮想マーカーを設定し、このマーカーにビューを復元します。
// @namespace          http://tampermonkey.net/
// @version            2024.12.22
// @icon               https://cdn-icons-png.flaticon.com/128/14417/14417460.png
// @author             Copiis
// @match              https://x.com/home
// @grant              none
// @license            MIT
// ==/UserScript==

(function () {
    let isAutomationActive = false;
    let isAutoScrolling = false;
    let savedTopPostTimestamp = null;
    let regularSaveInterval = null;

    document.addEventListener('DOMContentLoaded', () => {
        console.log("[DEBUG] Seite geladen. Starte regelmäßiges Speichern des obersten Beitrags...");
        startRegularTopPostSave();
    });

    const observer = new MutationObserver(() => {
        if (isAtTopOfPage() && !isAutomationActive) {
            console.log("[DEBUG] Scrollen zum oberen Ende erkannt. Automatik wird aktiviert.");
            extractTopPostTimestamp();
            isAutomationActive = true;
        }

        if (isAutomationActive) {
            const newPostsButton = getNewPostsButton();
            if (newPostsButton) {
                console.log("[DEBUG] 'Neue Beiträge anzeigen'-Button gefunden. Speichere obersten Beitrag und klicke.");
                extractTopPostTimestamp(); // Timestamp des obersten Beitrags speichern
                newPostsButton.click(); // Button klicken
                waitForNewPostsToLoad();
            }
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });

    window.addEventListener('scroll', () => {
        if (isAutoScrolling) {
            console.log("[DEBUG] Automatisches Scrollen erkannt. Scroll-Event ignoriert.");
            return;
        }

        if (window.scrollY === 0 && !isAutomationActive) {
            console.log("[DEBUG] Scrollen zum oberen Ende erkannt. Automatik wird aktiviert.");
            extractTopPostTimestamp();
            isAutomationActive = true;
        } else if (window.scrollY > 0 && isAutomationActive) {
            console.log("[DEBUG] Automatik deaktiviert, da nicht oben.");
            isAutomationActive = false;
        }
    });

    function startRegularTopPostSave() {
        regularSaveInterval = setInterval(() => {
            if (isAtTopOfPage()) {
                console.log("[DEBUG] Speichere regelmäßig Timestamp des obersten Beitrags...");
                extractTopPostTimestamp();
            }
        }, 1000); // Speichere alle 1000ms, wenn ganz oben
    }

    function isAtTopOfPage() {
        return window.scrollY === 0;
    }

    function getNewPostsButton() {
        return Array.from(document.querySelectorAll('button, span')).find((button) =>
            /neue Posts anzeigen|Post anzeigen/i.test(button.textContent.trim())
        );
    }

    function extractTopPostTimestamp() {
        const topPost = getTopVisiblePost();
        if (topPost) {
            savedTopPostTimestamp = getPostTimestamp(topPost);
            console.log("[DEBUG] Timestamp des obersten Beitrags gespeichert:", savedTopPostTimestamp);
        } else {
            console.log("[DEBUG] Kein oberster Beitrag gefunden.");
        }
    }

    function getPostTimestamp(post) {
        const timeElement = post.querySelector('time');
        return timeElement?.getAttribute('datetime') || null;
    }

    function getTopVisiblePost() {
        const posts = Array.from(document.querySelectorAll('article'));
        console.log(`[DEBUG] Anzahl gefundener Beiträge: ${posts.length}`);
        return posts.length > 0 ? posts[0] : null;
    }

    function waitForNewPostsToLoad() {
        console.log("[DEBUG] Warte auf das Laden neuer Beiträge...");
        const checkInterval = setInterval(() => {
            const matchedPost = findPostByTimestamp(savedTopPostTimestamp);
            if (matchedPost) {
                clearInterval(checkInterval);
                console.log("[DEBUG] Gespeicherter Beitrag nach Laden gefunden. Scrollen...");
                scrollToPost(matchedPost, 'end');
            }
        }, 100);
    }

    function findPostByTimestamp(timestamp) {
        if (!timestamp) return null;
        const posts = Array.from(document.querySelectorAll('article'));
        return posts.find((post) => {
            const timeElement = post.querySelector('time');
            const postTimestamp = timeElement?.getAttribute('datetime');
            return postTimestamp === timestamp;
        });
    }

    function scrollToPost(post, position = 'end') {
        console.log(`[DEBUG] Scrollen zum gespeicherten Beitrag (Position: ${position})...`);
        isAutoScrolling = true;
        post.scrollIntoView({ behavior: 'smooth', block: position });
        setTimeout(() => {
            isAutoScrolling = false;
            isAutomationActive = false;
            console.log("[DEBUG] Automatisches Scrollen beendet. Automatik deaktiviert.");
        }, 1000);
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址