Indexed Database APIを使ってスタンドアロンな付箋アプリを作ってみる

# 記事の趣旨

せっかく[Indexed Database APIについて勉強した](https://qiita.com/nk5jp/items/612693f14ab020ca0593)ので，それを使ってブラウザアプリを作ってみた．

## 1. 作ったアプリと技術要素

ブラウザにスティッキー（付箋紙）っぽいメモを残していけるツール．本当はカンバン方式の管理ツールを作りたかったんだけれど，力尽きたのでいったん保留．画面キャプチャは以下のとおり．

![kanban01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494245/43c63602-d037-ffa5-d787-c33a5d78f976.png)

使用した技術要素は以下のとおり．Vue.jsは初めて触るので，書籍と睨めっこしながらの対応．

- HTML
- CSS
- Vue.js
- Indexed Database API（敢えてライブラリ未使用）

ソースコードは[Githubのリポジトリ](https://github.com/nk5jp/brows_kanban)に置いているのでご自由にどうぞ．動作は「Chrome 79.0.3945.117」で確認．Edgeでも動作するがレイアウトが若干崩れる．

## 2. 操作方法

直感で扱えるレベルだと思うけれど，とりあえず以下のとおり．

1. cloneなりzip downloadなりでローカルに落としてきたら`kanban.html`を開く．
2. 付箋紙を新たに作りたいときは左下のプラスマークをクリック．
3. 付箋紙はドラッグ&ドロップで自由に移動可能．
4. 付箋紙をダブルクリックすると編集ウィンドウ（後述）が開く．
5. 編集ウィンドウでURLを登録しておけば，付箋紙左下の「URL」ボタンクリックでその画面に新タブで遷移可能．

![kanban02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494245/0d91a6dc-b154-b767-02b9-ce6448a79ffe.png)

## 3. 苦戦したポイント

スキル低いので，初歩的だったり，間抜けなものばかりだが，恥を忍んで書いていく．

### 3.1 Promise

以下の処理は，「DBへのコネクションを確立する」->「チケットを全件取得する」->「グローバル変数にチケット情報を詰め込む」という意図のコードになる．このうち，`createConnection()`および`getAllTickets()`は，`Promise`オブジェクトを返却する関数である．[先日の記事](https://qiita.com/nk5jp/items/612693f14ab020ca0593)でも述べたとおり，Indexed Database APIは非同期処理であるため，`Promise`を活用することで同期を図っている．

```javascript
    created: function() {
        createConnection().then(
            getAllTickets()
        ).then(
            (allTickets) => {
                allTickets.forEach(
                    ticket =>
                    {
                        tickets.push({
                            id: ticket.id,
                            text: ticket.text,
                            url: ticket.url,
                            categoryId: ticket.categoryId,
                            left: ticket.left,
                            top: ticket.top,
                            zIndex: ticket.zIndex,
                            deleted: false
                        })
                    }
                )
            }
        ).catch(
            (reason) => alert(reason)
        )
    }
```

さて，上記コードは一部が不正で，これだと同期処理にならない．どこかわかるだろうか．

答えは3行目．`getAllTicket()`と書いているが，ここは`getAllTicket`と，関数オブジェクトを指定しないといけない．

`promise.prototype.then()`は，あくまでメソッドであり，その引数指定するのはコールバック関数．ものすごく間抜けなミスだが，うっかりif/elseの構文と同じような感覚で記述してしまい，上記のミスに至ってしまった．

### 3.2 Bubbling

今回の付箋紙オブジェクトは，複数のレイヤで構成されているので，Bubblingを意識してイベントをハンドリングしないといけない．が，そもそもその辺りを全然理解しておらず，序盤はかなり意図しない挙動に苛まれた．たぶん今も多少バグは残っていると思われる．

Bubblingについては，[こちらのページの説明](https://ja.javascript.info/bubbling-and-capturing)がとてもわかりやすいと思う．定義を引用させていただくと以下のとおり．

> 要素上でイベントが起きると、最初にその上のハンドラが実行され、次にその親のハンドラが実行され、他の祖先に到達するまでそれらが行われます。

今回でいうと，例えば「URL」ボタンをクリックすると，続けて付箋紙自体にセットされたマウスダウンイベントが処理されることになる．後者は付箋紙の移動に関わる処理なので，付箋紙がワープするなどの謎挙動が頻発していた．

Bubblingを防ぎたい場合は，`event.stopPropagation()`を呼び出すことで，それ以降の親オブジェクトへの伝播を抑制することができる．

## 4. ペンディングしている事項

- 付箋紙の色の変更機能
  - categoryIdという死にパラメータがその名残
- ファイルサーバやローカルへのショートカットボタン
  - エクスプローラの直接操作は無理そうなので，パスをクリップボードにコピーする処理を想定
- ゴミ箱アイコンへのドロップによる付箋紙削除処理
  - 面倒臭くなって今は削除ボタンで代替
- カンバンボード化と，付箋紙へのステータス（ToDo，WIP，Done）導入
  - そもそもそれを目指したアプリだったので
- 複数タブ対応
  - 現在は複数タブでこのアプリを開いたら何が起きるか不明