2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Webに好きにリンクを追加するクロム拡張を作ってみた

Last updated at Posted at 2022-05-24

ツールの特徴

  • Web上の好きな場所にノートを張り付けられる
  • 同じノートを複数の場所に貼り付けることもできる
  • ノートを介してWeb上の複数の場所を相互リンクできる

Web上での調べもののまとめや、よく一緒に使う複数の場所をまとめたりに活用できます。
Webのブックマーク・クリッピング・アノテーションツールでしっくりくるのが無かったため、自作してみました。

chromeShopDescJa.png

利用技術と参考になった情報源

Webフロントエンドは素人なため、いろんな情報源をあさりながら手探りでの開発でした。今回採用した主な環境・ライブラリと参考資料は以下の通りです。

TypeScript

最初にJavaScriptで開発しましたが、複数クラスの処理が入り混じると効率が落ちていきました。効率を落とさずに開発を継続できたのは、TypeScriptに切り替えたからが一番大きいです。

TypeScriptの習得で一番参考になったのは、サバイバルTypeScriptで、素人にも非常に読みやすかったです。

JQuery UI

メンテナンスモードのため、長期的な利用を考えると迷いました。が、Webページを見ながら、その上にノートを重ねてメモできるという使用感を、モードレスダイアログで簡単に実現できたため、採用しました。これについては、公式のAPI文書やデモコードが一番参考になりました。

また、JQueryとの併用に難があるため、今流行りのReactは見送りました。モードレスダイアログ対応ライブラリがあればReactでも良いのですが、調べ方の問題か見つけられませんでした。表示に制約のあるモバイル対応を考えると、モードレスダイアログは筋が悪いという事なのでしょうか。

IndexedDB

ノートの記録場所は、ローカルとクラウドの二つの選択肢があります。クラウドだと、複数のマシンや人の間で簡単に共有ができるメリットがありますが、

  • 一人でも便利に使えないとクラウド以前の問題
  • クラウド版のメリットが出てくる規模だと、維持費がばかにならない
  • プライバシー・セキュリティや迷惑行為などの対策が追加で必要
  • 今回ただでさえ慣れないフロントエンドで不確定要素を増やしたくない

という事情を考慮し、まずはシンプルにローカルで完結するIndexedDBに記録する事にしました。IndexedDBで参考になったのは、MDN Web Docsです。普通に使うとコールバック地獄で、正常系の処理が追いにくくなるため、Promiseで浅くラップして利用しました。例えば癖のあるカーソルの処理だと、こんな感じです。

IdCursor.ts
export class IdCursor {
  private promise__: Promise<IDBCursor | null>
  constructor(readonly req__: IDBRequest<IDBCursor | null>) {
    this.promise__ = new Promise((resolve, reject) => {
      req__.onerror = ev => reject(ev2Err(ev))
      req__.onsuccess = () => resolve(req__.result)
    })
  }

  // 管理する状態の数を減らすため、カーソルを進めるのと情報の取得をまとめて処理。
  // 本当はn個スキップ→要素の取得としたいが、IDBCursorの仕様上最初の要素は必ず取得してしまうため、
  // やむなく要素の取得→n個スキップというまとめ方にした。
  async next__(afterSkip: number = 0): Promise<IDBValidKey | undefined> {
    // カーソル探索終了を待つ
    const cursor = await this.promise__
    if (!cursor) {
      return undefined
    }
    const key = cursor.key
    // 次回探索のためのカーソル設定
    this.promise__ = new Promise((resolve, reject) => {
      this.req__.onerror = ev => reject(new Error(JSON.stringify(ev)))
      this.req__.onsuccess = () => resolve(this.req__.result)
      if (afterSkip > 0) {
        cursor.advance(afterSkip + 1)
      } else {
        cursor.continue()
      }
    })
    return key
  }
}

クロム拡張リリースの参考

通常のJavaScriptと異なる点は、以下のようなサイトが参考になりました。

リリース後の感想

Webをさらに便利に使えるツールにまとめられた上、目的があるため技術の理解・習得が早く、フロントエンド技術も毛が生えた位には持って行けました。

一方、改善点もたくさん見えてきたので、徐々に対応していきたいと考えています。例えば

  • デザインをもっと洗練したい
  • PDFにもノートやリンクを付けたい
  • 複数のマシンや人で簡単に共有したい
  • ノートをUI表示するためDOMに追記する以上、他のクロム拡張などのスクリプトから見えるのは仕方ないと思っていたのか?
2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?