4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Chrome拡張のサイドパネルを活用してみる

4
Last updated at Posted at 2025-12-04

はじめに

今回はChrome拡張でサイドパネル機能を活用し、表示中のページ情報を取得してみました。

本記事では、Chrome拡張機能のManifest V3を使い、Side Panel APIでサイドパネルを開閉し、Content Scriptで表示中のWebページ情報を取得するサンプルを解説します。

なお、本記事では以下の扱いで用語を用いています。
サイドパネル:ChromeのSide Panel機能そのものを指します
サイドバー:サイドパネル上のコンテンツを指します

ファイル構成

Chrome-Sidebar/
├── manifest.json       # 拡張機能の基本的な情報、権限、サイドパネルの定義
├── background.js       # 拡張機能のバックグラウンド処理(サイドパネル開閉設定など)
├── icons/              # 拡張機能のアイコン画像群(16x16, 48x48, 128x128ピクセル)
│   ├── icon16.png
│   ├── icon48.png
│   └── icon128.png
└── sidebar/            # サイドバーのUIとロジックを格納するディレクトリ
    ├── sidebar.html    # サイドバーのHTML構造(ボタンや情報表示エリアなど)
    ├── sidebar.css     # サイドバーのスタイル定義
    └── sidebar.js      # サイドバー内のボタン操作や、現在開いているページ情報の取得ロジック

各種ファイル内容

manifest.json

manifest.json
{
  "manifest_version": 3,
  "name": "Qiita サイドパネルサンプル",
  "version": "1.0",
  "description": "現在開いているページの情報をサイドパネルに表示するサンプル拡張機能です。",
  "permissions": ["sidePanel", "tabs", "scripting"],
  "host_permissions": [
    "<all_urls>"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "side_panel": {
    "default_path": "sidebar/sidebar.html"
  },
  "action": {
    "default_title": "サイドパネルを開く"
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

permissions

permissions フィールドには、この拡張機能が利用するChrome APIの機能を宣言します。
これにより、Chromeは拡張機能がどのような操作を行うのかをユーザーに示すことができます。

  • sidePanel: Side Panel APIを利用するために必要です。これにより、Chromeのサイドパネルに拡張機能のUIを表示できます。
  • tabs: 現在開いているタブの情報(URLやタイトルなど)を取得するために必要です。chrome.tabs.query メソッドなどがこれに該当します。
  • scripting: Content Scriptを動的にウェブページに注入(実行)するために必要です。これにより、WebページのDOMにアクセスして情報を取得できるようになります。

host_permissions

host_permissions フィールドは、拡張機能がどのウェブサイトのURLに対して操作(Content Scriptの実行やデータ読み取りなど)を行うかを宣言します。

今回のサンプルでは、"<all_urls>" を指定しています。これは、すべてのウェブサイトに対してこの拡張機能が動作し、Content Scriptを注入してページの情報を取得できることを意味します。

【重要な注意点】
"<all_urls>" は非常に強力な権限です。実際の公開用拡張機能や、特定の用途に特化した拡張機能では、必要最小限のドメイン(例: "https://*.example.com/*""https://www.google.com/*")に限定しましょう。

background.js

今回のサンプルでは、Chromeツールバーの拡張機能アイコンをクリックした際の挙動としてサイドパネルの開閉を行っています。
background.jsにはこの開閉処理が記述されています。
image.png

background.js
// onInstalledは拡張機能インストール時に一度だけ実行
chrome.runtime.onInstalled.addListener(() => {
  // アイコンクリックでサイドパネルを開くようにChromeに設定します。
  // これにより、Chromeがネイティブに開閉(トグル)の動作を処理してくれます。
  chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })
    .catch((error) => console.error(error));
});

sidebar.html

サイドパネルに表示されるサイドバーのHTMLです。
情報取得用のボタンと取得情報を表示するエリアを用意しています。
image.png

sidebar.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Sidebar</title>
  <link rel="stylesheet" href="sidebar.css">
</head>
<body>
  <h1>サイドバーサンプル</h1>
  <p>現在のページの情報を取得します。</p>
  <button id="getInfoButton">情報を取得</button>
  <hr>
  <h2>ページ情報</h2>
  <pre id="pageInfo">ここに情報が表示されます</pre>

  <script src="sidebar.js"></script>
</body>
</html>

sidebar.css

サイドバー用の最小限のCSSを入れています。

sidebar.css
/* sidebar/sidebar.css */
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  padding: 15px;
  width: 300px;
  box-sizing: border-box;
}

h1 {
  font-size: 1.2em;
  margin-top: 0;
}

p {
    font-size: 0.9em;
}

button {
  width: 100%;
  padding: 10px;
  margin: 10px 0;
  cursor: pointer;
  border: 1px solid #ccc;
  background-color: #f0f0f0;
  border-radius: 4px;
}

button:hover {
    background-color: #e0e0e0;
}

pre {
  white-space: pre-wrap;
  word-break: break-all;
  background-color: #f8f9fa;
  border: 1px solid #dee2e6;
  padding: 10px;
  border-radius: 4px;
  font-size: 0.85em;
  color: #212529;
}

sidebar.js

サイドバーで必要なスクリプトを実装しています。
表示中のページのbodyを取得し、先頭500文字をサイドバーのページ情報エリアへ表示します。

また、最小限のエラー処理を入れています。

sidebar.js
// sidebar/sidebar.js

const getInfoButton = document.getElementById('getInfoButton');
const pageInfoElement = document.getElementById('pageInfo');

// ボタンのクリックイベントリスナーをasync関数に変更
getInfoButton.addEventListener('click', async () => {
  pageInfoElement.textContent = '情報を取得中...';

  // 現在アクティブなタブを取得
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

  // タブが取得できなかったり、Chromeの内部ページなどでは動作しないためガード
  if (!tab || !tab.id || tab.url.startsWith('chrome://')) {
    pageInfoElement.textContent = 'このページでは情報を取得できません。';
    return;
  }

  try {
    // Content Scriptを対象のタブに注入して実行
    const injectionResults = await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      // ページ内部で実行する関数。ページのbody.innerTextを返す。
      func: () => document.body.innerText,
    });

    if (injectionResults && injectionResults.length > 0 && injectionResults[0].result) {
      const bodyText = injectionResults[0].result;
      
      // 取得した情報をオブジェクトにまとめる
      const info = {
        title: tab.title,
        url: tab.url,
        // bodyは長すぎる可能性があるので、最初の500文字に要約
        body_summary: bodyText.substring(0, 500) + (bodyText.length > 500 ? '...' : ''),
      };
      
      // 整形して<pre>タグに表示
      pageInfoElement.textContent = JSON.stringify(info, null, 2);
    } else {
      pageInfoElement.textContent = 'ページの本文を取得できませんでした。';
    }
  } catch (error) {
    // 権限がないページなどでエラーになる場合がある
    pageInfoElement.textContent = `エラーが発生しました: ${error.message}\nページをリロードするか、別のタブで試してください。`;
    console.error(error);
  }
});

iconsフォルダー

本記事ではファイル内容は割愛します。
manifest.jsonで指定したアイコン画像を配置してください。
存在しない場合拡張機能のインストール時にエラーが発生します。
まずは小さな透明画像でもいいので配置してお試しください。

おまけ

ページ情報の取得は実現できました。

サイドパネル上にiframeなどでAIチャットサービスを読み込み、取得したページ情報をpostMessageで<iframe>に送信するなどをすることで、ページ要約を依頼するなど、高度な連携が実現できます。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?