Greasy Fork

Greasy Fork is available in English.

Facebook 乾淨化 & 加宽贴文区 & 播放连续短片时隐藏影片标题

删除FaceBook多余、不常使用的功能按钮和要素,使整个页面看起来更加简洁,加宽贴文区,让整体观感更好,播放连续短片时隐藏影片标题

安装此脚本
作者推荐脚本

您可能也喜欢FaceBook 贴文悬浮截图按钮

安装此脚本
// ==UserScript==
// @name         Facebook Cleaner & Expand post area & Hide video titles during Facebook Reels playback.
// @name:zh-TW   Facebook 乾淨化 & 加寬貼文區 & 播放連續短片時隱藏影片標題
// @name:zh-CN   Facebook 乾淨化 & 加宽贴文区 & 播放连续短片时隐藏影片标题
// @name:JA      Facebookのクリーンアップ & 投稿エリアの拡張 & 連続ショート動画再生時のタイトル非表示
// @namespace    http://tampermonkey.net/
// @version      6.6
// @description  Remove unnecessary and rarely used feature buttons and elements from Facebook to make the entire page look cleaner and more streamlined, Widen the post area to improve the overall visual experience, Hide video titles during Facebook Reels playback.
// @description:zh-TW 刪除FaceBook多餘、不常使用的功能按鈕和要素,使整個頁面看起來更加簡潔,加寬貼文區,讓整體觀感更好,播放連續短片時隱藏影片標題
// @description:zh-CN 删除FaceBook多余、不常使用的功能按钮和要素,使整个页面看起来更加简洁,加宽贴文区,让整体观感更好,播放连续短片时隐藏影片标题
// @description:JA  Facebookの不要であまり使われない機能ボタンや要素を削除し、ページ全体をよりシンプルに見せます。投稿エリアを広げ、全体的な見た目や読みやすさを向上させます。連続ショート動画を再生する際には、動画のタイトルを非表示にします。
// @author       chatgpt
// @match        https://www.facebook.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  // 預設開啟
  const autoExpandSidebar = GM_getValue('autoExpandSidebar', true);

  // 註冊開關功能到油猴選單
  GM_registerMenuCommand(`自動展開:${autoExpandSidebar ? "✅開啟" : "⛔關閉"}`, () => {
    const newSetting = !autoExpandSidebar;
    GM_setValue('autoExpandSidebar', newSetting);
    alert(`「自動展開」已${newSetting ? "開啟 ✅" : "關閉 ⛔"},重新整理後生效`);
    location.reload();
  });

  // === 🚫 禁止影片自動播放模組===
  (function preventVideoAutoplayModule() {
  const disableAutoplay = GM_getValue('disableVideoAutoplay', true);

   function isReelsVideo(video) {
  let el = video;
  for (let i = 0; i < 10; i++) {
    if (!el.parentElement) break;
    el = el.parentElement;
    if (el.innerHTML?.includes('/reel/') || el.innerHTML?.includes('/reels/')) {
      return true;
    }
  }
  return false;
  }

  GM_registerMenuCommand(`禁止影片自動播放:${disableAutoplay ? "✅開啟" : "⛔關閉"}`, () => {
    const newSetting = !disableAutoplay;
    GM_setValue('disableVideoAutoplay', newSetting);
    alert(`「禁止影片自動播放」已${newSetting ? "開啟 ✅" : "關閉 ⛔"},重新整理後生效`);
    location.reload();
  });

  if (!disableAutoplay) return;

  let lastUserInteractionTime = 0;

  // 使用者互動時記錄時間(滑鼠或鍵盤)
  ['click', 'keydown', 'mousedown'].forEach(eventType => {
    document.addEventListener(eventType, () => {
      lastUserInteractionTime = Date.now();
    }, true);
  });

  function wasRecentlyInteracted() {
    return Date.now() - lastUserInteractionTime < 1000; // 1 秒內有互動視為手動
  }

  function scanAndPauseVideos() {
    const videos = document.querySelectorAll('video');
    videos.forEach(video => {
    if (!video.__autoplayChecked) {
      video.__autoplayChecked = true;

      if (isReelsVideo(video)) {
        // 禁止 Reels 預覽播放
        video.play = () => {
          console.log('🎬 阻止 Reels video 播放');
          return Promise.resolve();
        };
        video.pause();
        video.autoplay = false;
        video.muted = true;
        video.removeAttribute("autoplay");
        video.removeAttribute("playsinline");
        return;
      }

      // 非手動播放的影片也禁止播放
      if (!video.paused && !wasRecentlyInteracted()) {
        video.pause();
        console.log('⛔ 阻止自動播放影片');
      }
    }
  });
  }

  // 偵測影片 play 行為,非手動觸發就阻止
  document.addEventListener('play', (e) => {
    const video = e.target;
    if (video.tagName === 'VIDEO') {
      setTimeout(() => {
        if (!wasRecentlyInteracted() && !video.paused) {
          video.pause();
          console.log('⛔ play事件:非使用者手動播放,已阻止');
        }
      }, 100);
    }
  }, true);

  // 初次執行 + 動態監聽影片新增
  scanAndPauseVideos();
  const observer = new MutationObserver(scanAndPauseVideos);
  observer.observe(document.body, { childList: true, subtree: true });
})();

  /***** === 第一階段:Facebook 清爽化(先執行)=== *****/
  (function cleanerPhase() {
    // 需要隱藏的左側選單文字
    const leftKeywords = [
    // 繁體中文
      '動態回顧', '我的珍藏', '我的收藏', 'Marketplace', '兒童版 Messenger',
      '玩遊戲', '近期廣告動態', '訂單和付款', '氣候科學中心', '募款活動', '籌款活動',
      '廣告管理員', 'Meta Quest 3S',
    // 簡體中文
      '那年今天', '收藏夹', '广告管理工具', '气候科学中心',
      '订单与支付', '玩游戏', '近期广告动态', '筹款活动', 'Messenger 少儿版',
    // 英文
      'Memories', 'Saved', 'Messenger Kids',
      'Gaming', 'Play games', 'Recent ad activity', 'Orders and payments',
      'Climate Science Center', 'Fundraisers', 'Ads Manager',
    //日文
      '思い出', '保存済み', '保存したで', '広告マネージャ', '広告マネージャー','気候学センター',
      '注文と支払い', 'ゲームをプレイ', '最近の広告アクティビティ','募金キャンペーン', '資金調達専門家', 'Messengerキッズ'
    ];

    // 「顯示更多」與「顯示較少」的相關文字
    const moreKeywords = ['顯示更多', '更多', '展开', 'See more', 'More', 'Show more', 'See More', 'MORE', 'SHOW MORE', 'もっと見る'];
    const lessKeywords = ['顯示較少', '收起', 'Show less', 'Show Less', 'Less', 'LESS', '表示を減らす'];

    // 其他要移除的元素選擇器
    const selectors = [
      'footer',
      'div[role="contentinfo"]',
      'div[aria-label="Facebook"] > div:last-child'
    ];

    // 確認是否為有側欄的主要頁面
    function isMainSidebarPage() {
      return !!document.querySelector('nav[role="navigation"], [role=navigation]');
    }

    // 根據文字內容隱藏左側欄目
    function hideLeftSidebarByText() {
      document.querySelectorAll('nav a[role="link"], [role=navigation] a[role="link"]').forEach(a => {
        let match = false;
        a.querySelectorAll('span.x1lliihq').forEach(span => {
          if (span.textContent && leftKeywords.includes(span.textContent.trim()) && span.children.length === 0) {
            match = true;
          }
        });
        if (match) a.style.display = 'none';
      });
    }

    // 根據標題文字隱藏右側欄
    function hideRightSidebarByTitle() {
      const rightKeywords = ['贊助', '赞助内容', '聯絡人', '联系人', '群組聊天室', '群聊', 'Sponsored', 'Contacts', 'Group conversations', '連絡先', 'グループチャット'];
      document.querySelectorAll('h3').forEach(h3 => {
        if (h3.textContent && rightKeywords.some(kw => h3.textContent.includes(kw))) {
          let parent = h3;
          for (let i = 0; i < 6; i++) if (parent.parentElement) parent = parent.parentElement;
          if (parent && parent.offsetWidth > 200) parent.style.display = 'none';
        }
      });
    }

    function removeRightSidebarComplementary() {
      const sidebar = document.querySelector('[role="complementary"]');
        if (!sidebar || !sidebar.parentElement) return;

    // 若為貼文頁(包含 photo、fbid、posts 等)
      const isPostPage =
            location.href.includes('/photo') ||
            location.href.includes('fbid=') ||
            location.href.includes('/posts/') ||
            location.href.includes('/permalink/');

    // 若是貼文頁,完全不刪除 complementary,避免移除貼文內文
        if (isPostPage) return;

    // 擴充語言支援(繁中、簡中、英文、日文)
      const contactKeywords = [
      '聯絡人', '联系人',
      '群組聊天室', '群聊',
      'Contacts', 'Group conversations',
      '連絡先', 'グループチャット'
    ];
      const sidebarText = sidebar.textContent || '';
      const isContactSidebar = contactKeywords.some(kw => sidebarText.includes(kw));

        if (isContactSidebar) {
            sidebar.parentElement.removeChild(sidebar);
            console.log('右側聯絡人區塊已刪除');
        }
    }

    // 隱藏左側選單中的 Marketplace 按鈕
    function removeMarketplaceButton() {
      document.querySelectorAll('a[aria-label="Marketplace"], a[href="/marketplace/?ref=app_tab"], a[href="/marketplace/"]').forEach(a => {
        let li = a;
        for (let i = 0; i < 5; i++) {
          if (li.parentElement && li.parentElement.tagName === 'LI') {
            li = li.parentElement;
            break;
          }
          if (li.parentElement) li = li.parentElement;
        }
        if (li.tagName === 'LI') li.remove();
      });
    }

    // 隱藏頁尾政策連結(例如隱私權政策、服務條款)
    function hidePolicyLinks() {
      const policyKeywords = ['隱私政策', '服務條款', '廣告', 'Ad Choices', 'Cookie', 'Meta © 2025', 'Privacy Policy', 'Terms'];
      document.querySelectorAll('footer, div[role="contentinfo"]').forEach(container => {
        policyKeywords.forEach(kw => {
          if (container.textContent.includes(kw)) container.style.display = 'none';
        });
      });
    }

    // 隱藏「顯示更多/較少」按鈕
    function removeMoreAndLessButtons() {
      document.querySelectorAll('[role="button"]').forEach(btn => {
        const spans = btn.querySelectorAll('span');
        for (const span of spans) {
          const text = span.textContent.trim().toLowerCase();
          if (moreKeywords.some(kw => text === kw.toLowerCase() || text.includes(kw.toLowerCase()))) {
            btn.style.display = 'none';
            break;
          }
          if (lessKeywords.some(kw => text === kw.toLowerCase() || text.includes(kw.toLowerCase()))) {
            btn.style.display = 'none';
            break;
          }
        }
      });
    }

    // 嘗試自動展開「顯示更多」左欄選單(加上開關限制)
    function tryExpandOnce() {
     if (!autoExpandSidebar) return; // ✅ 關閉開關時不自動展開
     if (!isMainSidebarPage()) return;

      let found = false;
      const btns = Array.from(document.querySelectorAll('nav[role="navigation"] [role="button"], [role=navigation] [role="button"]'));
     for (const btn of btns) {
       if (btn.offsetParent === null) continue;
      const spans = btn.querySelectorAll('span');
      for (const span of spans) {
        const text = span.textContent.trim().toLowerCase();
        if (moreKeywords.some(kw => text === kw.toLowerCase() || text.includes(kw.toLowerCase()))) {
        btn.click(); // ✅ 只在開關開啟時點擊展開
        found = true;
        setTimeout(removeMoreAndLessButtons, 800);
        break;
      }
    }
     if (found) break;
  }
}

    // 一次執行所有需要隱藏的元素處理
    function hideOtherElements() {
      selectors.forEach(selector => {
        document.querySelectorAll(selector).forEach(el => el.style.display = 'none');
      });
      hideLeftSidebarByText();
      hideRightSidebarByTitle();
      removeMarketplaceButton();
      hidePolicyLinks();
      removeRightSidebarComplementary(); // 加這一行
    }

    // 使用 debounce 控制節流,避免太頻繁操作 DOM
    function debounceHideAndExpand() {
      clearTimeout(window.__fbCleanerDebounceTimer);
      window.__fbCleanerDebounceTimer = setTimeout(() => {
        hideOtherElements();
        tryExpandOnce();
      }, 300);
    }

  function expandLeftSidebar() {
  const leftRail = document.querySelector('[aria-label="左側導覽列"]');
  if (!leftRail) return;

  // ✅ 一律刪除多餘項目
  const extraItems = leftRail.querySelectorAll('div[role="navigation"] > div:not(:first-child):not(:nth-child(2))');
  extraItems.forEach(el => el.remove());

  // ✅ 如果開關開啟,才展開左側
  if (autoExpandSidebar) {
    const collapseBtn = document.querySelector('div[aria-label="收合左側欄"]');
    if (collapseBtn) collapseBtn.click();
    }
  }

  // DOM 結構就緒後執行
  window.addEventListener('load', () => {
    setTimeout(expandLeftSidebar, 1000);
  });

    // 初次執行清理
    hideOtherElements();

    // 監聽頁面變動,動態移除新增的干擾元素
    const observer = new MutationObserver(debounceHideAndExpand);
    observer.observe(document.body, { childList: true, subtree: true });
  })();

  /***** === 第二階段:加寬貼文顯示區域(後執行)=== *****/

  // 預設寬度
  const defaultWidth = 1200; //預設1200px
  let postWidth = GM_getValue('postWidth', defaultWidth);

  // 註冊自訂寬度選單指令
  GM_registerMenuCommand(`設定貼文區寬度,目前寬度為${postWidth}px`, () => {
    const input = prompt("請輸入貼文寬度(數值,範圍 600~3000):", postWidth);
    const newWidth = parseInt(input);

    if (!isNaN(newWidth) && newWidth >= 600 && newWidth <= 3000) {
      GM_setValue('postWidth', newWidth);
      alert(`貼文寬度已設定為 ${newWidth}px,頁面將重新整理以套用設定。`);
      location.reload(); // 自動重新整理
    } else {
      alert("請輸入 600~3000 之間的有效數字!");
    }
  });

    // 加寬貼文主體容器 (使用固定寬度,避免閃爍)
    const styleWiden = document.createElement('style');
    styleWiden.textContent = `
      div.x193iq5w.xvue9z.xq1tmr.x1ceravr {
      width: ${postWidth}px !important;
      max-width: ${postWidth}px !important;
        margin-left: -60px !important;
        box-sizing: border-box !important;
        transition: none !important;
        will-change: width !important;
     }
      /* 限動框寬度 */
      div.x193iq5w.xgmub6v.x1ceravr {
      width: ${postWidth}px !important;
     }
      /* 左側縮排 */
      .xxc7z9f {
        max-width: 60px !important;
     }
    `;
    document.head.appendChild(styleWiden);

    // 標記已加寬貼文避免重複操作
    function applyWiden() {
     const posts = document.querySelectorAll('div.x193iq5w.xvue9z.xq1tmr.x1ceravr');
    posts.forEach(post => {
        if (!post.classList.contains('widened')) {
          post.classList.add('widened');
          // 只靠CSS強制固定寬度,不額外更動DOM
        }
     });
    }

    // 初始加寬
    applyWiden();

    // 建立一個 MutationObserver,用來監聽 DOM 結構的變化(貼文動態載入時觸發)
    const widenObserver = new MutationObserver(() => {
    // 清除之前尚未執行的延遲操作(防抖機制)
    clearTimeout(window.__fbWidenDebounce);
    // 設定一個新的延遲操作,在 200 毫秒後執行 applyWiden 函式(加寬貼文)
    window.__fbWidenDebounce = setTimeout(applyWiden, 200);
    });

    // 如果文件已經載入且有 body,就開始監聽整個 body 的 DOM 變化
    if (document.body) {
    widenObserver.observe(document.body, {
        childList: true, // 監聽子節點新增/刪除
        subtree: true    // 遞迴監聽整個子樹結構
    });
    }

    /***** === 第三階段:Reels 影片標題穩定處理 === *****/

    // 使用 WeakMap 儲存每個影片 video 對應的標題元素與上次播放狀態
    const videoTitleMap = new WeakMap();

    function monitorReelsPlayback() {
    // 取得目前頁面上所有可見的影片元素
    const videos = Array.from(document.querySelectorAll('video'))
    .filter(v => v.offsetParent !== null); // 過濾不可見影片

    videos.forEach(video => {
    let record = videoTitleMap.get(video); // 取得影片綁定資訊(若有)
    let titleBar = null;
    // 避免處理不是 Reels 區域的影片
    function isInsideReels(video) {
    let el = video;
    for (let i = 0; i < 10; i++) {
    if (!el.parentElement) break;
    el = el.parentElement;

    const classList = el.className || '';
    const hasCommonReelsBase = classList.includes('x1ey2m1c') && classList.includes('xh8yej3');
    const hasReelsVariants =
      classList.includes('x13a6bvl') ||
      classList.includes('xdhlvrg') ||
      classList.includes('x10l6tqk');

    if (hasCommonReelsBase && hasReelsVariants) {
      return true;
    }
  }
  return false;
}
    if (!isInsideReels(video)) return;

    // 判斷是否需要重新綁定(首次或 DOM 已被移除)
    const needsRebind = !record || !record.titleBar?.isConnected;

    if (needsRebind) {
      let el = video;

      // 往上找最多 10 層 DOM 結構,嘗試尋找標題元素
      for (let i = 0; i < 10; i++) {
        if (!el.parentElement) break;
        el = el.parentElement;

        // 搜尋包含常見 class 的容器當作標題欄位候選者
        const candidates = el.querySelectorAll(
          'div[class*="x1ey2m1c"][class*="x13a6bvl"], ' +
          'div[class*="x1ey2m1c"][class*="xdhlvrg"], ' +
          'div[class*="x1ey2m1c"][class*="xh8yej3"], ' +
          'div[class*="x1ey2m1c"][class*="x10l6tqk"]'
        );

        // 從候選中找到可能是影片標題的元素
        for (const c of candidates) {
          const text = c.innerText?.trim();
        // 排除含有播放控制項的區塊
          const hasControlButtons =
                c.querySelector('button[aria-label*="暫停"]') ||
                c.querySelector('button[aria-label*="播放"]') ||
                c.querySelector('button[aria-label*="靜音"]') ||
                c.querySelector('[role="slider"]');

          if (
              text && text.length > 2 &&
              c.offsetHeight > 0 && c.offsetWidth > 0 &&
              !hasControlButtons
          ) {
            titleBar = c;
            break;
          }
        }

        if (titleBar) break; // 找到就停止往上尋找
      }

      if (titleBar) {
        // 綁定影片與標題,並儲存目前播放狀態
        videoTitleMap.set(video, {
          titleBar,
          lastPaused: video.paused
        });

        // 根據當前是否為播放中,決定是否顯示標題
        titleBar.style.display = video.paused ? "" : "none";
      }

    } else {
      // 已有綁定 → 使用原有記錄
      titleBar = record.titleBar;
      const lastPaused = record.lastPaused;
      const isPaused = video.paused;

      // 播放狀態變化時才更新標題顯示與儲存狀態
      if (isPaused !== lastPaused) {
        titleBar.style.display = isPaused ? "" : "none";
        videoTitleMap.set(video, { titleBar, lastPaused: isPaused });
      }
    }
  });
}

// 每 300 毫秒執行一次,持續監控影片播放狀態並控制標題顯示
setInterval(monitorReelsPlayback, 300);

})();