ページのタイトルとURLを取得するChrome拡張を作った。

# 動機
ふとChrome拡張機能を作りたいと思った。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Chrome拡張作るか…</p>&mdash; Asa (a.k.a Otsun) (@a01sa01to) <a href="https://twitter.com/a01sa01to/status/1104580886060662784?ref_src=twsrc%5Etfw">March 10, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

で、何を作るかとなったときに、
Markdown つかうようになってきたけど、タイトルの取得に時間がかかる...！
そこで、タイトルやURLを数クリックで取得＆コピーできたらいいなと思い、作ってみました。

# 作成前
### タイトル取得時
タイトルの場合、
1. ブックマークのボタンを使用して名前から取得し、`Ctrl+C`する。
2. コンソールを開いて `document.title` を打って、範囲選択 + `Ctrl+C`する。
などが私のタイトル取得方法でした。

| Bookmark | Console |
|:------------------:|:------------------:|
| ![Bookmark_TtlGet.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/109700bd-e098-6428-8074-941b90ca4897.jpeg) | ![Console_TtlGet.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/a6231948-5700-7458-d40e-8432981c63e6.jpeg) |

( \` _ \` ).oO(ブラックテーマっていいよね...)

### URL取得時
URLバー（？）クリック + `Ctrl+C`が簡単。
コンソールで画像中の２つを打つのもあり。ほかにもありそう。

| Bar | Console |
|:------------------:|:------------------:|
| ![bar_URLGet.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/8af926d2-bc14-d007-7ccd-15aae0ea0f20.jpeg) | ![Console_URLGet.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/a5eb554b-5358-b177-b211-2e8a90b75382.jpeg)
 |

***いずれにしても、面倒くさい***

# 作成後
![ScreenShot.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/842cf91c-c15c-7319-77bd-8e2332269f93.jpeg)

出力を用途によって変えられる。

例えば、Google ( https://www.google.com ) であれば、

| Get Title | Get URL | Get Both with Markdown |
|:---------:|:--------|:----------------------:|
|  Google  | https://www.google.com/ | `[Google](https://www.google.com/)` |

といった具合にテキストボックスに出力され、横のコピーボタンをクリックすればコピーされる。便利。

*ちなみに `This Extension is open source` です。Please `Improve this Extension.`*

# ソースコード
[a01sa01to/titleAndURL_Picker](https://github.com/a01sa01to/titleAndURL_Picker)
こちらで公開していますが、コメントなしなので、こちらでざっくり解説しようと思います。

## ファイル構成
 - manifest.json
 - index.html
 - script.js
 - style.css

## manifest.json

```json:manifest.json
{
    "name": "Title & URL Picker",  // 拡張機能の名前（必要）
    "version": "1.0",  // バージョン（必要）

    "icons": {"128": "icon128.png"},  // アイコン（推奨）

    "description": "Get Title And URL of a Page.",  // 拡張機能の説明（推奨）

    "browser_action": {
        "default_title": "Title & URL Picker",  // 拡張機能のアイコンをホバーしたときに出るテキスト（任意）
        "default_popup": "index.html"  // 拡張機能アイコンをクリックしたときに出るページ（任意）
    },

    "permissions": ["activeTab"],  // 許可を求める（任意。この拡張に関しては使わなかったかも...？）

    "author": "Asa",  // 作成者（任意）

    "manifest_version": 2  // マニフェストのバージョン（必須。現在は2が推奨されている。）
}
```
このファイルは、拡張機能を作るのに必要不可欠なファイルです。
（JSONにコメントは付けられないので赤線が引かれてあると思われます）

 > Every extension has a JSON-formatted manifest file, named manifest.json, that provides important information.
[Manifest File Format - Google Chrome](https://developer.chrome.com/extensions/manifest) 

これを訳すと、「すべての拡張機能はmanifest.jsonというJSONマニフェストファイルがある。それは重要な情報を渡すもの。」

ここがずれると、ほぼすべての動作が変わるといっても過言ではないほど重要だと思います。

ちなみに、拡張機能のページ([chrome://extensions/](chrome://extensions/))では、以下のように表示されます。

![extensionPreview.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/542946ab-9445-b32c-cc4f-a3b3a24bc4fa.jpeg)

表示されているのは...
name, version, icon, description のみですかね

## index.html

```html:index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="style.css">
        <script src="script.js"></script>
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
        <script src="clipboard.min.js"></script>
    </head>
    <body>
        <h1>Title & URL Picker</h1>
        <button class="ttl">Get Title</button>
        <button class="url">Get URL</button>
        <button class="bmark">Get Both with Markdown</button>
        <input type="text" readonly id="result">
        <button class="copy" data-clipboard-target="#result" data-clipboard-action="copy"><i class="far fa-copy"></i></button>
        <div class="msg">
            <p class="copied">Copied!</p>
            <p class="failed">failed...</p>
        </div>
        <p>This Extension is open source. <a href="https://github.com/a01sa01to/titleAndURL_Picker" target="_blank">Improve this Extension.</a></p>
    </body>
</html>
```
![ScreenShot.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/f046ac32-197e-8eb7-e0ce-4de722977d35.jpeg)

見比べたら大体わかるかと思います。
今回、[Font Awesome](https://fontawesome.com/) と [clipboard.js](https://clipboardjs.com/)を使用しました。
どちらもコピーボタンのデザイン＆実装に使用しています。

## style.css

```css:style.css
@import url('https://fonts.googleapis.com/css?family=Muli');
*{
    font-family: 'Muli', sans-serif;
    color: #fff;
    box-sizing: border-box;
    margin: auto;  /* 中央揃え */
}
h1{
    text-align: center;  /* 中央揃え */
    grid-area: header;
}
button{
    font-size: 16px;
    background: #666;
    margin: 8px;
    border-radius: 10px;
    padding: 10px;
    width: 1fr;  /* Grid 特有。面白い。 */
    cursor: pointer;
}
input{
    font-size: 18px;
    color: #555;
    padding: 8px;
    height: 40px;
    grid-area: result;
    width: 95%;  /* 左右に余白を持たせたかった。 */
}
body{
    --height: 60px;  /* CSSでも変数を使えるらしい。 */
    width: max-content;  /* 子要素の 最大幅+Margin が bodyの最大幅 */
    padding: 8px;
    background: #333;
    display: grid;
    grid-template-rows: var(--height) var(--height) var(--height) var(--height) var(--height) 10px 20px;  /* See 変数の使い方 */
    grid-template-columns: 1fr 75px 50px;
    grid-template-areas:
        "header header header"
        "ttlBtn ttlBtn ttlBtn"
        "urlBtn urlBtn urlBtn"
        "markBtn markBtn markBtn"
        "result result copyBtn"
        "...... msg msg"
        "notice notice notice";
}
button.copy{
    width: 40px;
    height: 40px;
    border-radius: 20px;  /* 丸にしたかった。楕円にならないようにwidth=heightになっている */
    font-size: 24px;
    padding: 4px;
    grid-area: copyBtn;
}

button.ttl{
    grid-area: ttlBtn;
}
button.url{
    grid-area: urlBtn;
}
button.bmark{
    grid-area: markBtn;
}
p{
    width: max-content;
    grid-area: notice;
    text-align: right;
    z-index: 2;
}
div.msg{
    z-index: 300;  /* 究極に上に表示させている。 */
    grid-area: msg;
    display: none;
    width: 100px;
    height: 30px;
    margin-right: 0;
    border-radius: 15px;
    padding: 4px 18px;
    font-size: 18px;
    background: #eee;
}
p.copied, p.failed{
    color: #333;
    z-index: 350;  /* さらに上に表示 */
    display: none;
    grid-area: unset;
    position: absolute;
}
```
これは...個人の好みの問題です...

今回、レイアウトに初めてGridを使ってみました。
Gridについてはこちらの方の記事がとても分かりやすかったです！
[CSS Grid Layout を極める！（基礎編） - Qiita](https://qiita.com/kura07/items/e633b35e33e43240d363)

あとCSSって変数使えるんですねー。
[CSS カスタムプロパティ (変数) の使用 - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/Using_CSS_variables)

私は割とMuliというフォントが好きです。というかsans-serif体がいい。
[Muli - Google Fonts](https://fonts.google.com/specimen/Muli)


## script.js
```js:script.js
let Data = {"Title": "", "URL": ""}  // とりま格納する変数

chrome.tabs.getSelected(tab=>{  // 現在のタブを取得
    Data.Title = tab.title;  // tabに現在のタブが格納されている（？）。
    Data.URL = tab.url;    // tab.titleには現在開いているタブのページタイトルが、tab.urlにはURLが格納されている。
    console.log(`Title: ${Data.Title}`);  // 出力は、「ポップアップを検証」で見れる。
    console.log(`URL: ${Data.URL}`);
});

window.addEventListener('load',()=>{  // 拡張機能アイコンがクリックされて拡張機能ポップアップページが読み込まれたとき
    const txtBox = document.querySelector('input');
    document.querySelector('button.ttl').addEventListener('click',()=>{  // クリックされたときにテキストボックスに出力
        txtBox.value = Data.Title;
    });
    document.querySelector('button.url').addEventListener('click',()=>{
        txtBox.value = Data.URL;
    });
    document.querySelector('button.bmark').addEventListener('click',()=>{
        txtBox.value = `[${Data.Title}](${Data.URL})`;
    });

    const cb = new ClipboardJS('button.copy');  // Clipboard.js
    const msgContainer = document.querySelector('div.msg');
    const msgSuccess = document.querySelector('p.copied');
    const msgFailed = document.querySelector('p.failed');
    cb.on("success", function(e){  // コピーに成功
        console.log('Copied Successfully.', e);
        msgContainer.style.display = "block";  // 表示
        msgSuccess.style.display = "block";
        setTimeout(()=>{
            msgSuccess.style.display = "none";  // 3sec後に非表示
            msgContainer.style.display = "none";
        },3000);
    });
    cb.on("error", function(e) {  // コピーに失敗
        console.error('Failed to Copy.', e);
        msgContainer.style.display = "block";
        msgFailed.style.display = "block";
        setTimeout(()=>{
            msgFailed.style.display = "none";
            msgContainer.style.display = "none";
        },3000);
    });
})
```
Clipboard.jsの使い方は、こちらが参考になりました！
[JavaScriptでコピーをする方法(clipboard.jsの使い方)](https://syncer.jp/clipboardjs)

Chrome拡張の作り方は公式をはじめ、様々なサイトを拝見しましたが、DOMを取得しない私の作りたい機能では、`SendMessage` や `Content_Script` などは必要なかったようです。
今後作る拡張には組み込みたい...

# 動作チェック

やっぱり(?)Googleで動作チェック。

### 1. タブのページチェック
![chk01.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/16b6ec5e-976c-a67b-bdb7-cd59f56028c1.jpeg)
URLは https://www.google.com/ になっています。
もちろんタイトルは「Google」。

(^^) .oO( W3 誕生日おめでとう！！！) ` // 撮影日：03/12`

### 2. レイアウトチェック
![chk02.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/f576baaa-7b8b-0d92-7408-2b14b2b7b126.jpeg)
自分好みなデザインになっています。
ダークテーマ、最高！！！

### 3. Button「Get Title」動作チェック
![chk03.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/cb56347e-ff12-2c59-c917-8854ce573d16.jpeg)
タイトルである「Google」の文字がしっかりとテキストボックスに出力されています。

### 4. Button「Get URL」動作チェック
![chk04.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/606a2569-6e1b-6862-8eff-b64367bfea1a.jpeg)
これもしっかりURLが出力されていますね。

### 5. Button「Get Both with Markdown」動作チェック
![chk05.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/12fa7811-474e-5af0-5ca0-b53ecf0fe21c.jpeg)
しっかりとMarkdownの書式にあったものになっています。

// Markdownでは `[タイトル](URL)`とすることで以下のリンクが作成されます。
// [タイトル](URL)

### 6. Button「Copy」動作チェック
これはClipboard.jsを用いて実装したのですが、果たして...？

![chk06.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/95058e09-b964-ce49-d5eb-8f1785a47edc.jpeg)
一応Copiedの文字は表示されました。が、実際にコピーされているのでしょうか...


### 7. コピーされているかチェック
下に貼り付けました。
[Google](https://www.google.com/)

その画像がこちら。
![chk07.jpg](https://qiita-image-store.s3.amazonaws.com/0/279132/41593bb5-e506-7a89-b248-859d53bb97dc.jpeg)

一応ペーストした部分はハイライトしておきました、
しっかりコピーされていました。Clipboard.js 優秀！

---

チェック終了。
すべて自分の期待通りに動いてくれました。

# 拡張機能を公開
~~...はしなくてもいいかなと。完全に自己満足なので。~~

（2022.01.30追記）
と思っていたのですが、公開しました: https://chrome.google.com/webstore/detail/apegdmeimjlklboalimnaokfnnngajcg
よければお使いください～:grinning:（Manifest v3に対応しました...！）

ソースコードは[GitHubで公開](https://github.com/a01sa01to/titleAndURL_Picker)しています。

一応インストール手順も記述しておきましたので、使用したい方はそちらをもとにインストールしてみてください！