開いているウインドウの全タブのページタイトルとURLを取得するChrome拡張作った

# はじめに
作り始めてから色々調べてたら「GetTabInfo」など既に公開されてるのもので実現可能でした。ただ、一度Chrome拡張を自作することにこそ意味があると思って開発を進めました。
あとちなみに今回でChrome拡張の開発は初めてでした。まあ、HTMLやJavaScriptにはある程度慣れてるので躓いたのは普通のサイトならできてもChrome拡張では制限されてできないことへの対策ですかね・・・。

# それはそうと、なんで必要なん？
まずひとつ、私自身がタブ魔で、常に開いているタブの数が半端ない（開発中に開いてる全ウインドウのタブの数をコンソール出力したら80くらいあったわ。）というのはある。そのせいでめちゃくちゃChromeちゃんがRAMをもりもり食べてるのは仕方ない。

あとはまあ、少し前からQiitaでこうやって記事書いたりしてますが、いちいち全ての参考文献のwebページのタイトルとURLをコピペするのは面倒くさすぎる。一応今回で完成には至ったので、今回の記事からワンクリックで参考文献が貼り付けられます。
アルル「やったね、カーくん！！」
カーバンクル「ぐぐー！」
というぷよキャラの勝利セリフが一瞬横切りました。が、見なかったことにしましょう。

# 機能紹介
 現時点ではまだ設定を変える機能は未実装なので直接`JavaScript`のコードを弄ることになりますが、今後、設定を変えられるようにする予定です。自分が使いたいので。笑

##  テンプレート機能で表現の幅が広がる！

`getTitleAndURL.js`15行目の変数を書き換えることで、コピーしたいタイトル、URLの形式（コロン区切りやMarkDown形式など）に変えることができます。

%%title%%：ページのタイトルに置換されます。
%%URL%%：ページのURLに置換されます。  
仕様上、「'」「"」「\」を含めるには、エスケープシーケンス「\'」「\"」「\\」を用いる必要があります。

例えば、MarkDown形式に対応させる（そのまま貼り付けて使えるようにする）には、`[%%title%%](%%URL%%)`のようにします。デフォルトでは、`[%%title%%](%%URL%% \"%%title%%\")`となっており、`a`タグの`title`属性を付与させています。

## 区切り文字編集機能でより快適に！

`getTitleAndURL.js`14行目の変数を書き換えることで、一つ一つのページのタイトル&URL間の区切りを指定できます。これにより、コンマ区切りなどにもできます。デフォルトでは改行となっています。

## これがなくっちゃ始まらない！開いている全てのタブのページ情報のクリップボードへのコピー！

getTitleAndURLofALLTabsを動かしたウィンドウ内の全てのページのタイトルとURL（テンプレートと区切り文字に基づく）を表示し、「copy」ボタンのクリックでクリップボードにコピーすることができます。

# ソースコード
ひとまず仮のものなのでUIなどは非常にシンプルです。CSS不使用なのでHTML初心者に優しい（？）
GitHubでも公開しています。→[yukuduri/getTitleAndURLofALLTabs](https://github.com/yukuduri/getTitleAndURLofALLTabs "yukuduri/getTitleAndURLofALLTabs: 開いているウインドウ内のすべてのタブのタイトルとURLを取得するGoogleChrome拡張機能。")

```manifest.json
{
  "name": "get Title&URL of ALLTabs",
  "author": "yukuduri",
  "version": "1.0.0",
  "description": "開いているウインドウ内のすべてのタブのタイトルとURLを取得します。",
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "browser_action":{
    "default_icon": {
      "19": "icon19.png"
    },
    "default_title": "get Title&URL of ALLTabs",
    "default_popup": "popup.html"
  },
  "permissions": [
    "activeTab",
    "tabs",
    "clipboardWrite"
  ],
  "manifest_version": 2
}
```

```popup.html
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <script src="getTitleAndURL.js"></script>
  </head>
  <body>
    <h2>get Title＆URL of ALLTabs</h2>
    <label>現在のウインドウのタブ数：<input id="numOfTabs" type="number" readonly></label><br><br>
    <label>取得したタイトルとURL：<textarea id="txt"></textarea></label><br>
    <button id="copy">copy</button>
  </body>
</html>
```

```getTitleAndURL.js
const createTextFromTemplate = (title, url, template) => {
  let txt = template;

  txt = txt.replace(/%%title%%/g,title);
  txt = txt.replace(/%%URL%%/g,url);
  return txt;
}


const run = () => {
  //現在のウインドウのタブをすべて取得
  chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT},(tabs) => {
    let txt = '';
    const delimiter = '\n';  //区切り文字
    const template = '[%%title%%](%%URL%% \"%%title%%\")'; //テンプレ

    document.querySelector('#numOfTabs').value = tabs.length;

    tabs.forEach((tab,i) => {
      if(i!=0) txt += delimiter;  //最初は区切り文字不要
      console.log(tab.title+" "+tab.url);
      txt += createTextFromTemplate(tab.title,tab.url,template);
    });

    document.querySelector('#txt').value = txt;

  });
}

const copy = () => {
  const copyText = document.querySelector('#txt');
  copyText.select();
  document.execCommand('copy');
}

window.addEventListener('load',()=>{
  run();
  document.querySelector("#copy").addEventListener("click", copy);
})

```


# 簡単な解説
## `manifest.json`
細かいところは参考文献を見ていただくことにして、需要度の高いものをいくつか。

`"permissions"`：`"activeTab"`,`"tabs"`,`"clipboardWrite"`の3つを指定。`"activeTab"`は不必要なときに動作させないため、`"tabs"`は実行時のタブ以外の情報を取り出すために必要、`"clipboardWrite"`は`document.execCommand('copy');`を使うなら必須ではないが付けておいてね、ってことみたいなので付けた。

あ、ちなみにアイコンはフリーソフトのInkscapeで作りました。

## `popup.html`
省略。次の`getTitleAndURL.js`のときに少し触れます。

## `getTitleAndURL.js`

```javascript
const createTextFromTemplate = (title, url, template) => {
  let txt = template;

  txt = txt.replace(/%%title%%/g,title);
  txt = txt.replace(/%%URL%%/g,url);
  return txt;
}
```
`template`に含まれる文字列「%%title%%」を全て`title`に、「%%URL%%」を全て`url`に、それぞれ置換します。

```javascript
const run = () => {
  //現在のウインドウのタブをすべて取得
  chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT},(tabs) => {
    let txt = '';
    const delimiter = '\n';  //区切り文字
    const template = '[%%title%%](%%URL%% \"%%title%%\")'; //テンプレ

    document.querySelector('#numOfTabs').value = tabs.length;

    tabs.forEach((tab,i) => {
      if(i!=0) txt += delimiter;  //最初は区切り文字不要
      console.log(tab.title+" "+tab.url);
      txt += createTextFromTemplate(tab.title,tab.url,template);
    });

    document.querySelector('#txt').value = txt;

  });
}
```

現在のウインドウのタブをすべて取得して実行します。`chrome.windows.WINDOW_ID_CURRENT`には今開いているブラウザウィンドウのidが格納されているのでこれを渡せば今開いているウインドウの全てのタブを取得できます。ここでは`tabs`に`tab`の配列が入っています。詳しくは省略しますが、ともかく、`tab.title`でそのタブのページタイトル`tab.url`でURLが取得できます。あとはそれを利用してコピーするための文字列を生成します。生成したら`textarea`にぶち込みます。ついでに今のウインドウで開いてるタブ数を表示しようとしましたが、Chrome拡張の制限から、`popup.html`のHTMLを`innerHTML`などでは扱えないようでした。なので`input`要素で実現しています。


```javascript
const copy = () => {
  const copyText = document.querySelector('#txt');
  copyText.select();
  document.execCommand('copy');
}
```
生成して出力された文字列をクリップボードにコピーします。
`document.execCommand('copy');'は全ての機種で動作するわけではなかったはずですが、まあPC版Chromeでの動作であればまず問題ないはずなので大丈夫でしょう。

```javascript
window.addEventListener('load',()=>{
  run();
  document.querySelector("#copy").addEventListener("click", copy);
})
```
アドレスバー横のChrome拡張のアイコンをクリックして`popup.html`が表示されるときに実行する場所です。開いたと同時に取得から`textarea`への挿入を終わらせてしまいます。
あとは、copyボタンがクリックされたときにクリップボードにコピーさせるだけです。

# owata
今回は以上になります。
Chrome拡張の設定の件でまたもう少しやって再び記事を書こうかなとか思ってますが、徹夜で作業してましたし眠いのでこれから寝ます。
誤字とかあったら起きてから直しますので、コメントに書いていただけるなどすると助かります。おやすみなさい。

#参考文献
今回作成した拡張機能で生成した文字列を以下に貼り付けてしまいましょう。
今回も、多くの記事を参考にさせていただきました。ありがとうございます。

[ページのタイトルとURLを取得するChrome拡張を作った。 - Qiita](https://qiita.com/a01sa01to/items/bd7b18b4ec3dc6c46b32 "ページのタイトルとURLを取得するChrome拡張を作った。 - Qiita")
[Chrome 拡張機能のマニフェストファイルの書き方 - Qiita](https://qiita.com/mdstoy/items/9866544e37987337dc79#options_page "Chrome 拡張機能のマニフェストファイルの書き方 - Qiita")
[Chromeブラウザの拡張機能を作ってみたい初心者向けに開発方法を紹介！【サンプルあり】 - Qiita](https://qiita.com/guru_taka/items/37a90766f4f845e963e5 "Chromeブラウザの拡張機能を作ってみたい初心者向けに開発方法を紹介！【サンプルあり】 - Qiita")
[Chrome拡張機能作成〜超基礎編〜 - Qiita](https://qiita.com/Ryo_Suzuki/items/d247008888ef67bdeda8 "Chrome拡張機能作成〜超基礎編〜 - Qiita")
[特定の文字列を全て置換する[Javascript] - Qiita](https://qiita.com/DecoratedKnight/items/103ab57431b6c448e535 "特定の文字列を全て置換する[Javascript] - Qiita")
[Chromeアドオン開発のチュートリアル：　ページの背景色を変えるアドオン（Getting Started Tutorial） - 主に言語とシステム開発に関して](https://language-and-engineering.hatenablog.jp/entry/2018/10/22/Chrome%E3%82%A2%E3%83%89%E3%82%AA%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%EF%BC%9A_%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE "Chromeアドオン開発のチュートリアル：　ページの背景色を変えるアドオン（Getting Started Tutorial） - 主に言語とシステム開発に関して")
[クリップボードとのやりとり - Mozilla | MDN](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard "クリップボードとのやりとり - Mozilla | MDN")
[EventTarget.addEventListener() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener "EventTarget.addEventListener() - Web API | MDN")
[chrome.windows - Google Chrome](https://developer.chrome.com/extensions/windows#current-window "chrome.windows - Google Chrome")
[Declare Permissions - Google Chrome](https://developer.chrome.com/extensions/declare_permissions "Declare Permissions - Google Chrome")
[The activeTab permission - Google Chrome](https://developer.chrome.com/extensions/activeTab#what-activeTab-allows "The activeTab permission - Google Chrome")
[chrome.windows-Google Chrome](https://developer.chrome.com/extensions/windows#property-WINDOW_ID_CURRENT "chrome.windows-Google Chrome")


あ、これは以前私が書いた記事ですが、一通りまとまっているので自分でも重宝しています。
[JavaScript基礎文法 - Qiita](https://qiita.com/yukuduri/items/cff4a1f9c85114600425 "JavaScript基礎文法 - Qiita")


ちなみにこんな感じで取得できています。

```
[ページのタイトルとURLを取得するChrome拡張を作った。 - Qiita](https://qiita.com/a01sa01to/items/bd7b18b4ec3dc6c46b32 "ページのタイトルとURLを取得するChrome拡張を作った。 - Qiita")
[Chrome 拡張機能のマニフェストファイルの書き方 - Qiita](https://qiita.com/mdstoy/items/9866544e37987337dc79#options_page "Chrome 拡張機能のマニフェストファイルの書き方 - Qiita")
[Chromeブラウザの拡張機能を作ってみたい初心者向けに開発方法を紹介！【サンプルあり】 - Qiita](https://qiita.com/guru_taka/items/37a90766f4f845e963e5 "Chromeブラウザの拡張機能を作ってみたい初心者向けに開発方法を紹介！【サンプルあり】 - Qiita")
[Chrome拡張機能作成〜超基礎編〜 - Qiita](https://qiita.com/Ryo_Suzuki/items/d247008888ef67bdeda8 "Chrome拡張機能作成〜超基礎編〜 - Qiita")
[特定の文字列を全て置換する[Javascript] - Qiita](https://qiita.com/DecoratedKnight/items/103ab57431b6c448e535 "特定の文字列を全て置換する[Javascript] - Qiita")
[Chromeアドオン開発のチュートリアル：　ページの背景色を変えるアドオン（Getting Started Tutorial） - 主に言語とシステム開発に関して](https://language-and-engineering.hatenablog.jp/entry/2018/10/22/Chrome%E3%82%A2%E3%83%89%E3%82%AA%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%EF%BC%9A_%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE "Chromeアドオン開発のチュートリアル：　ページの背景色を変えるアドオン（Getting Started Tutorial） - 主に言語とシステム開発に関して")
[クリップボードとのやりとり - Mozilla | MDN](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard "クリップボードとのやりとり - Mozilla | MDN")
[EventTarget.addEventListener() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener "EventTarget.addEventListener() - Web API | MDN")
[chrome.windows - Google Chrome](https://developer.chrome.com/extensions/windows#current-window "chrome.windows - Google Chrome")
[Declare Permissions - Google Chrome](https://developer.chrome.com/extensions/declare_permissions "Declare Permissions - Google Chrome")
[The activeTab permission - Google Chrome](https://developer.chrome.com/extensions/activeTab#what-activeTab-allows "The activeTab permission - Google Chrome")
[chrome.windows-Google Chrome](https://developer.chrome.com/extensions/windows#property-WINDOW_ID_CURRENT "chrome.windows-Google Chrome")
[JavaScript基礎文法 - Qiita](https://qiita.com/yukuduri/items/cff4a1f9c85114600425 "JavaScript基礎文法 - Qiita")
```