FB 最相關留言自動改成所有留言

2025/04/13

Categories: 程式 Tags:

前言

在 FB 上看到臉友抱怨爲什麼留言系統預設是最相關,每次都要點擊切換,很麻煩。

安裝 tampermonkey

這軟體是拿來讓使用者可以自己寫腳本,可以讓網頁變成自定義。

Eddie 這次寫的腳本就是在 tampermonkey 的基礎上做出來的,請先下載。

Chrome 擴充的下載連結

打開開發人員模式

在「設定」-「擴孔功能」-「管理擴充功能」裡面,打開「開發人員模式」。

這是必要的,讓 tampermonkey 有權限操作你想要執行的腳本。

開啓 tampermonkey 界面

點擊 「tampermonkey 圖標」-「控制檯」,即可進入後臺界面。

接著點擊「+」製作新腳本。

複製以下:

// ==UserScript==
// @name         Facebook - Default to All Comments
// @name:zh-TW   Facebook - 預設顯示所有留言
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  Automatically changes the default comment sort on Facebook from "Most Relevant" to "All Comments".
// @description:zh-TW 自動將 Facebook 貼文留言排序從「最相關」切換為「所有留言」。
// @author       Eddie Lu using Gemini 2.5 pro
// @match        *://*.facebook.com/*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- 設定 ---
    const CHECK_INTERVAL_MS = 1500; // 每隔多少毫秒檢查一次頁面 (例如 1.5 秒)
    const CLICK_DELAY_MS = 300;   // 點擊排序按鈕後,等待多少毫秒再點擊選項

    // --- 需要尋找的文字 (需要與你的 Facebook 語言介面對應) ---
    // 這些是常見的文字,如果你的介面不同,可能需要修改
    const mostRelevantTexts = ["Most Relevant", "最相關"]; // 目前顯示為「最相關」時的文字
    const allCommentsTexts = ["All Comments", "所有留言", "Newest", "最新"]; // 要點擊的選項文字 (包含 "Newest" 因為有時 FB 合併了)

    // --- 尋找排序按鈕的策略 ---
    // Facebook 結構常變,這裡結合多種方式提高成功率
    // 1. 透過 aria-label (無障礙標籤)
    // 2. 透過按鈕內的文字內容
// --- 尋找排序按鈕的策略 (更新) ---
    // 優先尋找 role="button" 的 div,再檢查文字
    // aria-label 作為備用策略
    const sortButtonSelectors = [
        'div[role="button"]', // *** 主要目標:尋找所有 role="button" 的 div ***
        'span[role="button"]', // 也考慮 span 按鈕的可能性
        // 保留 aria-label 作為其他 FB 版本的備用方案
        'div[role="button"][aria-label*="Sort comments by"]',
        'div[role="button"][aria-label*="留言排序依據"]',
        'div[role="button"][aria-label*="Comment sort order"]',
    ];

    // --- 輔助函數:模擬真實點擊 ---
    function simulateClick(element) {
        const mouseEventInit = { bubbles: true, cancelable: true, view: window };
        const mouseDownEvent = new MouseEvent('mousedown', mouseEventInit);
        const mouseUpEvent = new MouseEvent('mouseup', mouseEventInit);
        const clickEvent = new MouseEvent('click', mouseEventInit);
        element.dispatchEvent(mouseDownEvent);
        element.dispatchEvent(mouseUpEvent);
        element.dispatchEvent(clickEvent);
    }

    // --- 主要執行函數 ---
    function findAndSwitchComments() {
        // 尋找所有可能的排序觸發按鈕
        let potentialTriggers = [];
        sortButtonSelectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(el => {
                // 如果是 span,嘗試找到它外層可點擊的 button
                let clickableElement = el.closest('div[role="button"], span[role="button"]');
                if (!clickableElement && el.tagName === 'SPAN') {
                   // 如果 span 本身不是按鈕,檢查它的內容是否符合
                   const currentText = el.textContent?.trim();
                   if (currentText && mostRelevantTexts.some(text => currentText.includes(text))) {
                       // 向上查找可點擊的父元素
                       clickableElement = el.closest('div[role="button"], span[role="button"], div[tabindex="0"]'); // 擴大範圍
                   }
                } else if (el.tagName === 'DIV' && el.getAttribute('role') === 'button') {
                   // 如果本身就是 button div
                   clickableElement = el;
                }

                if (clickableElement && !potentialTriggers.includes(clickableElement)) {
                    potentialTriggers.push(clickableElement);
                }
            });
        });

        potentialTriggers.forEach(trigger => {
            // 檢查按鈕目前的文字是否為 "最相關"
            const currentButtonText = trigger.textContent?.trim();
            const needsSwitch = currentButtonText && mostRelevantTexts.some(text => currentButtonText.includes(text));

            // 加上一個標記避免重複處理同一個按鈕 (防止快速連點)
            if (needsSwitch && !trigger.dataset.switchInProgress) {
                console.log("發現 '最相關' 排序按鈕:", trigger, "準備切換...");
                trigger.dataset.switchInProgress = 'true'; // 標記處理中

                // 模擬點擊排序按鈕以打開選單
                simulateClick(trigger);

                // 等待選單彈出
                setTimeout(() => {
                    // 尋找「所有留言」或「最新」選項
                    let allCommentsOption = null;
                    // 選單通常在 body 下,所以全局搜索
                    const menuItems = document.querySelectorAll('div[role="menuitemradio"], div[role="menuitem"], div[role="option"]'); // 包含可能的角色

                    menuItems.forEach(item => {
                        const itemText = item.textContent?.trim();
                        if (itemText && allCommentsTexts.some(text => itemText.includes(text))) { // <--- 修改這裡,從 === 改成 includes                             // 確保選項是可見的 (一個基本的檢查)
                             if (item.offsetParent !== null || item.closest('[role="dialog"], [role="menu"]')) {
                                allCommentsOption = item;
                                return; // 找到就停止
                             }
                        }
                    });

                    if (allCommentsOption) {
                        console.log("找到 '所有留言/最新' 選項:", allCommentsOption, "點擊中...");
                        // 模擬點擊「所有留言」選項
                        simulateClick(allCommentsOption);
                        console.log("已切換為 '所有留言/最新'.");
                         // 成功切換後,過一段時間再移除標記,允許它在需要時再次被檢查
                         setTimeout(() => {
                              delete trigger.dataset.switchInProgress;
                         }, 1000); // 等待 1 秒後移除標記
                    } else {
                        console.log("錯誤:點擊了排序按鈕,但找不到 '所有留言/最新' 選項。可能是 Facebook 介面變動。");
                        // 如果找不到選項,也要移除標記,允許下次重試
                        delete trigger.dataset.switchInProgress;
                    }
                }, CLICK_DELAY_MS);
            } else if (trigger.dataset.switchInProgress && !needsSwitch) {
                // 如果標記還在,但文字已經不是 "最相關",表示可能已切換成功或被用戶手動切換
                delete trigger.dataset.switchInProgress;
            }
        });
    }

    // --- 定期執行檢查 ---
    console.log("Facebook [預設顯示所有留言] 腳本已啟動。");
    // 使用 MutationObserver 監聽 DOM 變化,比 setInterval 更有效率
    const observer = new MutationObserver((mutationsList, observer) => {
        // 不用檢查具體的 mutation,只要有變化就嘗試執行一次
        findAndSwitchComments();
    });

    // 設定觀察目標為整個 body,監聽子節點和子樹的變化
    observer.observe(document.body, { childList: true, subtree: true });

    // 也可以保留一個定時器作為備用,以防 MutationObserver 遺漏某些情況 (例如頁面滾動加載)
    setInterval(findAndSwitchComments, CHECK_INTERVAL_MS * 2); // 定時器的頻率可以設低一些

    // 頁面剛載入完成時也執行一次
    setTimeout(findAndSwitchComments, 1000);

})();

點擊「檔案」-「儲存」後退出檔案。

啓用腳本

記得啓用腳本,再到 FB測試吧!

>> Home