読者です 読者をやめる 読者になる 読者になる

DRYな備忘録

Don't Repeat Yourself.

たぶん世界一簡単なIndexedDBのサンプル

WebSQL先輩…

localStorageのようなシンプルなkvsじゃなくて、検索を伴うようなブラウザで永続化できるストレージあるかな、そういえばWebSQLってどうなってるんだろう?と思って調べたら、

あらまあ

代わりにIndexedDBを使えということらしい. なにそれ食えんの.

IndexedDBとは?

どうやら検索キーを指定してドキュメントをつっこめるストレージっぽい.

IndexedDB is a way for you to persistently store data inside a user’s browser.

IndexedDB を使用する - Web API インターフェイス | MDN

仕様が定まってないときに書かれたものなのか、インターネッツに転がってるサンプルがよく分からないので、今ヒットする仕様など読みながらコメント多めに書きました.

サンプル

http://www.html5rocks.com/ja/tutorials/indexeddb/todo/

これをもとに、2015/01/30 に書きました、念のため

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <script type="text/javascript" src="./main.js"></script>
    </head>
    <body>
        <input type="text" id="todo-text" value="" />
        <input type="submit" value="add" onclick="module.add();" />
        <ul id="todo-list">
        </ul>
    </body>
</html>

main.js

var module = {
    db: null,
    renderer: function(todo) {
        var li = document.createElement('li');
        li.innerHTML = todo.text + ':' + todo.timeStamp;
        document.getElementById("todo-list").appendChild(li);
    }
};

// 初期化のメソッドを定義してみる 
module.init = function() {
    // ここで返るrequestは、「DBをopenするリクエスト」という意味
    var req = window.indexedDB.open('my_db', 103);
    // onupgradeneededは、indexedDB.openの第二引数のversionが
    // 既存のものよりも大きいときだけ呼ばれる
    // なので、StoreしたいObjectのSchemeを変えるときなどに使えばよろしい
    req.onupgradeneeded = function(ev) {
        var db = ev.target.result;
        ev.target.transaction.onerror = function(err) { console.log("XXX0", err); };
        // Scheme変えるわけだし、既に存在してるなら削除しとく
        // これをしないと、Table already existsに似たエラーを吐く
        if (db.objectStoreNames.contains('todo')) {
            db.deleteObjectStore('todo');
        }
        // 改めてつくる
        var store = db.createObjectStore('todo', {keyPath:'timeStamp'});
        console.log("XXX1", store);
    };
    // 「DBをopenするリクエスト」が成功に終われば、
    // 得られた結果はDBなので、保持しておく
    // あとはこの単一オブジェクトを使えばよろしい
    req.onsuccess = function(ev) {
        module.db = (ev.target) ? ev.target.result : ev.result;
        // ま、ついでだしgetAllすっか
        module.getAll(module.renderer);
    };
};

// TODOを追加するメソッドを定義してみる
module.addTodo = function(text) {
    var db = module.db;
    // DBからObjectStoreへのトランザクションを生成する
    // この段階で"todo"というObjectStoreをつくってないと
    // 当然、Table name not found に似たエラーを吐く
    var tx = db.transaction(["todo"],"readwrite");
    // このトランザクション内でアクティブなObjectを生成する
    var store = tx.objectStore("todo");
    // putするリクエストを生成
    var req = store.put({text: text, timeStamp: Date.now()});
    // 「putするリクエスト」が成功したら...
    tx.oncomplete = function() { module.getAll(module.renderer); };
    // 「putするリクエスト」が失敗したら...
    tx.onerror = function(err) { console.log("xxx2", err); };
};

// TODOをすべて取得するメソッドを定義してみる
module.getAll = function(renderer) {
    if (renderer) document.getElementById('todo-list').innerHTML = '';
    // このへんは同じ
    var db = module.db;
    var tx = db.transaction(["todo"],"readwrite");
    var store = tx.objectStore("todo");
    // keyPathに対して検索をかける範囲を取得
    var range = IDBKeyRange.lowerBound(0);
    // その範囲を走査するカーソルリクエストを生成
    var cursorRequest = store.openCursor(range);
    // カーソルリクエストが成功したら...
    cursorRequest.onsuccess = function(e) {
        var result = e.target.result;
        // 注)走査すべきObjectがこれ以上無い場合
        //     result == null となります!
        if (!!result == false) return;
        // ここにvalueがくる!
        console.log(result.value);
        if (renderer) renderer(result.value);
        // カーソルを一個ずらす
        result.continue();
    }
    // カーソルリクエストが失敗したら...
    cursorRequest.onerror = function(err) {
        console.log("XXX3", err);
    }
};

module.add = function() {
    var text = document.getElementById('todo-text').value;
    module.addTodo(text);
};

(function(){
    module.init();
})();

こうなる

f:id:otiai10:20150130020339p:plain

雑感

慣れだ

Client-Side Data Storage: Keeping It Local

Client-Side Data Storage: Keeping It Local

HTML5ガイドブック 増補改訂版 Google Expert Series

HTML5ガイドブック 増補改訂版 Google Expert Series