ブラウザの「あとで見る」機能をChrome拡張機能を使って実装する。


この記事は[Zeals Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zeals) 9日目の記事になります。

はじめまして。Zealsでバックエンドエンジニアのインターンをしている大学3年生の@tomowarkarです。

日頃の少しきになることを解決するために作ったツールについて書いていきたいと思います。

# TL;DR
- Chrome拡張は簡単に作れるぞ。
- webサイトのあとで見る機能をChrome拡張機能を使って実装したぞ。
- HTML, CSS, JavaScriptしか使っていないので誰でも作れるはずだぞ！！

# 動作
まずデモ動画を見ていきます。

以下の動画のように、ワンクリックで開いているwebページをあとで見るリストに登録でき、

あとで見るリストの並べ替えができ、

削除もできるといった拡張機能を作りました。

![gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235435/af079dcf-72f3-0fac-bd11-38773efc1fd3.gif)

# 前提
ぼく自身よく複数ウィンドゥにまたがりブラウザでタブをたくさん開いてしまうということがよくあり、それを少しでも管理したかった。

タブには残したくない。ブックマークに登録するほどでもない。でも一時保存はしておきたいし、あとから絶対見返すページ。そんなページを管理したく作ったのがこのあとで見る機能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235435/b7791923-add9-1f5d-5666-94cdbf12a722.png)

# 最低限満たしたかった仕様
- 複数ウィンドウでリストの状態保持
- あとで見るリストに追加
- ワンクリックでタイトルとURLを追加
- あとで見るリストから削除
- あとで見るリストの並び替え

あとで見る機能への追加、削除は当然として、何か自分が作ったことがないような新しい機能を実装したかったので、ドラッグ&ドロップでリストの並び替えができるという要件を入れてみました。

技術スタック
-
- HTML, CSS
- JavaScript

あまりフロントエンドフレームワークに慣れていないことと、できるだけ最低限で実装したかったのもあり、素のJSで書きました。

ソースコード
-
<div align="center"><a href="https://github.com/tomowarkar/chrome_extensions/tree/master/watch_later"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/tomowarkar/chrome_extensions.png" width="600px"></a></div>

使い方
-
### 使い方1
1. レポジトリをクローン
2. chromeブラウザで`chrome://extensions`と検索
3. 左上のパッケージ化されていない拡張機能を読み込むから`~/chrome_extensions/watch_later/`を選択

```bash
$ git clone git@github.com:tomowarkar/chrome_extensions.git
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235435/7d301d3c-d113-2886-1213-9a797d5171eb.png)


ソースコード抜粋
-
普段JavaScriptやフロントエンドを書かないのでコードが汚いのはご愛嬌。

まずはクロム拡張機能の最低限を作成します。

### クロム拡張機能の作成
この3つのファイルを作成して、拡張機能として読み込ませるだけで拡張機能ができてしまいます。

思っていたより10倍は簡単ですね。

```bash
.
├── index.html
├── manifest.json
└── script.js
```
```manifest.json
{
  "name": "{ .FunctionName }",
  "version": "1.0.0",
  "manifest_version": 2,

  "permissions": [
    "activeTab"
  ],

  "browser_action": {
      "default_title": "{ .FunctionTitle }",
      "default_popup": "index.html"
  },
  "author": "{ .YourName }"
}
```

### 現在のタブのURLとタイトルを取得
試しに現在のタブのURLとタイトルを取得するコードを書いていきます。

```index.html

<button id="btn1">alert url and title</button>
<script src="script.js"></script>
```
```script.js
document.querySelector('#btn1').addEventListener('click', () => {
  chrome.tabs.query({'currentWindow': true, 'active': true}, tbs => {
    alert(tbs[0].title + "\n" + tbs[0].url)
  });
});
```
ボタンつくってゴニョゴニョっと書くだけでこんな感じ。しっかりタイトルとURLが取得できています。幸先いいですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235435/ff88fb7d-e1dc-a0cb-c376-c3d73f3db6cf.png)


### JavaScriptでのドラッグ・アンド・ドロップ
リストの追加削除は特に変わったことはしていないので割愛します。木になる方は[ソースコード](https://github.com/tomowarkar/chrome_extensions/tree/master/watch_later)までどうぞ。

さて、問題のドラッグ&ドロップです。

どのように実装するか考えていたところ、ドラッグ&ドロップはHTML5の仕様でネイティブサポートされているようです。

これは使わない手はないですね。

```dNd.js
let dragStatus = { "from": "", "to": "" }

function handleDragStart(e) {
  dragStatus.from = this.id
}

function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault(); // Necessary. Allows us to drop.
  }
}

function handleDrop(e) {
  dragStatus.to = this.id
  swapObject(dragStatus)
  dragStatus.from = ""
  dragStatus.to = ""
}

const setDragEvent = () => {
  document.querySelectorAll('.card').forEach(col => {
    col.addEventListener('dragstart', handleDragStart, false);
    col.addEventListener('dragover', handleDragOver, false);
    col.addEventListener('drop', handleDrop, false);
  });
}
```

ドラッグ開始時に開始する場所を保存し、ドラッグ終了時にドラッグ終了場所のオブジェクトと入れ替えるといった実装にしてみました。

### CSSでの文字のオーバーフロー処理
タイトルを全文載せると当然スタイルが崩れてしまいます。

全文は載せなくてもいいので、スタイルは保持したいと思いCSSを使った文字のオーバーフロー処理を調べたのですが、こちらも簡単にできるようです。

```style.css
.overflow {
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
  max-width:195px;
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235435/3764c2fb-fd1b-d366-c0db-2134bec00ef6.png)

そんなこんなで完成となります。抜粋として上がっていない部分も全て[ソースコード](https://github.com/tomowarkar/chrome_extensions/tree/master/watch_later)にあるので、よければご覧ください。

# 最後に
もともとクロム拡張機能を使って何か作ってみたいと思っていたので日頃の悩みを解消しつつこの拡張機能が作流ことができとても満足です。

また他にもブラウザのメモ機能や, タイトルURLをマークダウン形式で吐き出したりと拡張機能のポテンシャルの高さを実感。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235435/a1a8feb3-f367-d1e5-c17f-0b2d701083fd.png)

しかし、もう少し権限を追加してやれば、複数タブ、ウィンドウにまたがっての履歴や簡単行動ログまで取れてしまうので、拡張機能を追加する際は権限欄に気をつけないといけませんね。

今回作ったあとで見る機能では`chrome://extensions`などの一部カスタムURIが使えないので、余裕があれば直していこうと思います。

HTML, CSSそしてJavaScriptは普段あまり書かないので、おかしなコードや間違えがあればご指摘いただけると嬉しいです。

機能追加のPRもお待ちしています笑

[Zeals Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zeals) 9日目
Written by @tomowarkar 
# 参考
[HTML Standard #dnd](https://html.spec.whatwg.org/multipage/dnd.html#dnd)
[ネイティブ HTML5 ドラッグ＆ドロップ - HTML5 Rocks](https://www.html5rocks.com/ja/tutorials/dnd/basics/)