您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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.
当前为
- // ==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.21.1
- // @icon https://cdn-icons-png.flaticon.com/128/14417/14417460.png
- // @author Copiis
- // @match https://x.com/*
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function () {
- let isAutomationActive = false;
- let isAutoScrolling = false; // Status für automatisches Scrollen
- let savedTopPostInfo = null; // Speichert Name, Handler und erstes Wort des Beitrags
- // Starte die kontinuierliche Überprüfung, sobald die Seite geladen wird
- document.addEventListener('DOMContentLoaded', () => {
- console.log("[DEBUG] Seite geladen. Starte Überprüfung für sichtbaren Handler...");
- checkForVisibleHandler();
- });
- // Funktion, um kontinuierlich nach einem sichtbaren Handler zu suchen
- function checkForVisibleHandler() {
- const checkInterval = setInterval(() => {
- const topPost = getTopVisiblePost();
- if (topPost) {
- savedTopPostInfo = getPostInfo(topPost);
- console.log("[DEBUG] Sichtbarer Beitrag gefunden und gespeichert:", savedTopPostInfo);
- clearInterval(checkInterval); // Stoppe die Überprüfung, sobald ein Beitrag gefunden wurde
- }
- }, 100); // Überprüfe alle 100ms
- }
- const observer = new MutationObserver(() => {
- if (isAtTopOfPage() && !isAutomationActive) {
- console.log("[DEBUG] Scrollen zum oberen Ende erkannt. Automatik wird aktiviert.");
- extractPostInfoFromTopPost();
- isAutomationActive = true;
- }
- if (isAutomationActive) {
- const newPostsButton = getNewPostsButton();
- if (newPostsButton) {
- console.log("[DEBUG] 'Neue Posts anzeigen'-Button gefunden, wird geklickt.");
- newPostsButton.click();
- 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.");
- extractPostInfoFromTopPost();
- isAutomationActive = true;
- } else if (window.scrollY > 0 && isAutomationActive) {
- console.log("[DEBUG] Automatik deaktiviert, da nicht oben.");
- isAutomationActive = false;
- }
- });
- 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 extractPostInfoFromTopPost() {
- const topPost = getTopVisiblePost();
- if (topPost) {
- savedTopPostInfo = getPostInfo(topPost);
- console.log("[DEBUG] Oberster Beitrag gespeichert:", savedTopPostInfo);
- } else {
- console.log("[DEBUG] Kein oberster Beitrag gefunden.");
- }
- }
- function getPostInfo(post) {
- const nameElement = post.querySelector('a[role="link"] div[dir="ltr"] span');
- const name = nameElement?.textContent?.trim() || "Unbekannt";
- const handlerElement = post.querySelector('a[role="link"]');
- const handler = handlerElement?.getAttribute("href") || "Unbekannt";
- const postContentElement = post.querySelector('div[data-testid="tweetText"]'); // Text des Beitrags
- const postContent = postContentElement?.textContent?.trim() || "";
- const firstWord = postContent.split(/\s+/)[0]; // Erstes Wort des tatsächlichen Beitrags
- return { name, handler, firstWord };
- }
- 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 = findPostByInfo(savedTopPostInfo);
- if (matchedPost) {
- clearInterval(checkInterval);
- console.log("[DEBUG] Gespeicherter Beitrag nach Laden gefunden. Scrollen...");
- scrollToPost(matchedPost, 'end'); // Scrollt so, dass der gespeicherte Beitrag am unteren Rand sichtbar ist
- }
- }, 100);
- }
- function findPostByInfo(postInfo) {
- if (!postInfo) return null;
- const posts = Array.from(document.querySelectorAll('article'));
- return posts.find((post) => {
- const nameElement = post.querySelector('a[role="link"] div[dir="ltr"] span');
- const name = nameElement?.textContent?.trim();
- const handlerElement = post.querySelector('a[role="link"]');
- const handler = handlerElement?.getAttribute("href");
- const postContentElement = post.querySelector('div[data-testid="tweetText"]'); // Text des Beitrags
- const postContent = postContentElement?.textContent?.trim() || "";
- const firstWord = postContent.split(/\s+/)[0]; // Erstes Wort des tatsächlichen Beitrags
- return (
- name === postInfo.name &&
- handler === postInfo.handler &&
- firstWord === postInfo.firstWord // Verhindert Duplikate
- );
- });
- }
- function scrollToPost(post, position = 'end') {
- console.log(`[DEBUG] Scrollen zum gespeicherten Beitrag (Position: ${position})...`);
- isAutoScrolling = true; // Setzt den Status, dass ein automatisches Scrollen läuft
- post.scrollIntoView({ behavior: 'smooth', block: position });
- setTimeout(() => {
- isAutoScrolling = false; // Scrollen abgeschlossen
- isAutomationActive = false;
- console.log("[DEBUG] Automatisches Scrollen beendet. Automatik deaktiviert.");
- }, 1000); // Warte, bis das Scrollen abgeschlossen ist
- }
- })();