FBTitle Fix

Replaces generic Facebook tab title with the actual page or group name

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         FBTitle Fix
// @namespace    https://github.com/2u841r/FBTitle-Fix
// @version      1.0.1
// @description  Replaces generic Facebook tab title with the actual page or group name
// @author       Zubair Ibn Zamir
// @license      MIT
// @match        *://*.facebook.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  function getPageName() {
    // Primary: h1 button div (old page/group name header)
    const h1btn = document.querySelector('h1 div[role="button"]');
    if (h1btn) {
      const text = h1btn.childNodes[0]?.textContent?.trim();
      if (text) return text;
    }

    // Fallback: h1 text (groups: h1 > span). Skip hidden/junk h1s like the
    // visually-hidden "Facebook" logo heading.
    for (const h1 of document.querySelectorAll('h1')) {
      const text = h1.textContent?.trim();
      if (!text || text === 'Facebook') continue;
      if (h1.getBoundingClientRect().width === 0) continue;
      return text;
    }

    // Fallback: 2026-07 layout, page header has no h1 anymore.
    // Tail of the header container path; atomic class hashes may break on FB redesigns.
    const span = document.querySelector('div.x1cy8zhl.x1614ocl > div > div > span > div');
    if (span) {
      const text = span.textContent?.trim();
      if (text) return text;
    }

    // Last resort: structural walk, no classes. First short standalone text
    // block inside main content, above the fold (header area).
    const main = document.querySelector('div[role="main"]');
    if (main) {
      const candidates = main.querySelectorAll('span > div');
      for (const el of candidates) {
        if (el.children.length > 0) continue;
        if (el.getBoundingClientRect().top > 600) break;
        const text = el.textContent?.trim();
        if (text && text.length >= 2 && text.length <= 100) return text;
      }
    }

    return null;
  }

  function updateTitle() {
    const name = getPageName();
    if (name && !document.title.startsWith(name)) {
      document.title = name + ' | Facebook';
    }
  }

  const observer = new MutationObserver(() => updateTitle());
  observer.observe(document.body, { childList: true, subtree: true });

  const _pushState = history.pushState.bind(history);
  history.pushState = function (...args) {
    _pushState(...args);
    setTimeout(updateTitle, 500);
  };

  window.addEventListener('popstate', () => setTimeout(updateTitle, 500));

  updateTitle();
})();