📝

Slack のナビゲーションバーを非表示にする Chrome 拡張機能を作ってみた

に公開

Web 版の Slack で左側に表示されているナビゲーションバーを非表示にしてみました。

1. 拡張機能のフォルダとファイル作成

ローカル PC 内に任意のフォルダを作成し、以下のファイルを作成します。

  • manifest.json
  • content.js

今回は以下のようなフォルダ構成にしました。

slack-nav-hider/
├── manifest.json
├── content.js

2. ファイルの編集

Slack Tweaks · GitHub
スクリプトの処理は上記 GitHub を参考にしました。

まずは manifest.json に以下の内容を張り付けて保存します。

manifest.json
{
  "manifest_version": 3,
  "name": "Slack Nav Hider",
  "version": "0.1.0",
  "description": "Hide navigation bars in Slack web app.",
  "content_scripts": [
    {
      "matches": ["https://app.slack.com/*", "https://*.slack.com/*"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}

次に content.js に以下の内容を張り付けて保存します。

content.js
// content.js
(function () {
  "use strict";

  // 実際にナビゲーションを非表示にする処理
  function tryHideNav() {
    try {
      const helpBtn = document.querySelector('[data-qa="top-nav-help-button"]');
      const userBtn = document.querySelector('[data-qa="user-button"]');

      // 必要な要素が見つからなければ false を返して再試行させる
      if (!helpBtn || !userBtn) return false;

      // あなたがコンソールでやっていた DOM 操作(要素移動)
      const afterNode =
        helpBtn.parentNode &&
        helpBtn.parentNode.parentNode &&
        helpBtn.parentNode.parentNode.parentNode;
      const userContainer = userBtn.parentNode && userBtn.parentNode.parentNode;
      if (afterNode && userContainer) {
        try {
          afterNode.insertAdjacentElement("afterend", userContainer);
        } catch (e) {
          // 既に移動済みの可能性は無視
        }
      }

      // --- 修正ポイント1: より柔軟なセレクタ ---
      // 複数の可能性を試す
      let tabRail = document.querySelector(".p-tab_rail");
      if (!tabRail) {
        tabRail = document.querySelector('[data-qa-id="tab-rail"]');
      }
      if (!tabRail) {
        tabRail = document.querySelector(
          '[role="navigation"] [data-qa*="tab_rail"]'
        );
      }

      if (tabRail) {
        tabRail.style.display = ""; // 自体は表示

        // --- 修正ポイント2: より堅牢なボタン検索 ---
        const buttons = tabRail.querySelectorAll("button");
        buttons.forEach((btn) => {
          const qa = btn.getAttribute("data-qa") || "";
          const ariaLabel = btn.getAttribute("aria-label") || "";

          // ホームとDMを判定する複数の方法
          const isHome =
            qa.includes("home_button") ||
            ariaLabel.includes("ホーム") ||
            ariaLabel.includes("Home");
          const isDM =
            qa.includes("dms_button") ||
            qa.includes("direct_messages") ||
            ariaLabel.includes("メッセージ") ||
            ariaLabel.includes("Messages");

          if (isHome || isDM) {
            btn.style.display = ""; // 残す
          } else {
            btn.style.display = "none"; // 消す
          }
        });
      }

      const controlStrip = document.querySelector(".p-control_strip");
      if (controlStrip) controlStrip.style.display = "none";

      const workspace = document.querySelector(
        ".p-ia4_client .p-client_workspace--including_tab_rail"
      );
      if (workspace) {
        try {
          workspace.style.gridTemplateAreas =
            "p-client-workspace p-client-workspace";
          workspace.classList.add("p-theme_background");
        } catch (e) {
          // 無視
        }
      }

      return true;
    } catch (err) {
      console.error("tryHideNav error:", err);
      return false;
    }
  }

  // 初回実行
  if (
    document.readyState === "complete" ||
    document.readyState === "interactive"
  ) {
    tryHideNav();
  } else {
    window.addEventListener("DOMContentLoaded", tryHideNav);
  }

  // --- 修正ポイント3: MutationObserverの最適化 ---
  const observer = new MutationObserver((mutations, obs) => {
    // 関連のある変化のみ処理
    const isRelevant = mutations.some((m) => {
      const target = m.target;
      return (
        target.classList &&
        (target.classList.contains("p-tab_rail") ||
          target.classList.contains("p-control_strip") ||
          target.closest(".p-tab_rail") ||
          target.closest("[role='navigation']"))
      );
    });

    if (isRelevant) {
      tryHideNav();
    }
  });

  observer.observe(document.documentElement || document.body, {
    childList: true,
    subtree: true,
    attributes: true,
    attributeFilter: ["data-qa", "style", "class"],
  });

  // single-page app 対応
  (function () {
    const _push = history.pushState;
    history.pushState = function () {
      const ret = _push.apply(this, arguments);
      setTimeout(tryHideNav, 200);
      return ret;
    };
    const _replace = history.replaceState;
    history.replaceState = function () {
      const ret = _replace.apply(this, arguments);
      setTimeout(tryHideNav, 200);
      return ret;
    };
    window.addEventListener("popstate", () => setTimeout(tryHideNav, 200));
  })();

  // 定期的なリトライ
  (function () {
    let attempts = 0;
    const maxAttempts = 30;
    const interval = setInterval(() => {
      attempts++;
      if (tryHideNav() || attempts >= maxAttempts) clearInterval(interval);
    }, 1000);
  })();
})();

3. Chrome で読み込む

手順 1,2 完了後、Chrome の拡張機能のページから手順 1 で作成したフォルダを読み込みます。

4. 動作確認

手順 3 完了後、Web 版の Slack を開き、ナビゲーションバーが非表示になれば OK です。
すでに Slack を開いている場合はリロード後にナビゲーションバーが非表示になれば OK です。

まとめ

今回は Slack のナビゲーションバーを非表示にする Chrome 拡張機能を作ってみました。
どなたかの参考になれば幸いです。

参考資料

Discussion