ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

Web開発 - JavaScript: IndexedDBを使ってクライアントサイドのデータベースを操作する

初めに

 IndexedDBとは、ウェブブラウザー内でクライアントサイドのデータベースを操作するためのAPIです。本記事はIndexedDBの使い方を簡単にまとめます。

IndexedDB

IndexedDBとは

 IndexedDBはウェブブラウザー内でクライアントサイドのデータベースを操作するためのAPIです。IndexedDBを使用すると、大量の構造化されたデータを保存し、検索、更新、削除することができます。

使い方

基本パターン

  1. データベースを開く。
  2. データベース内に、オブジェクトストアを作成する。
  3. データの追加や取り出しといった、データベース操作のトランザクションを開始して、リクエストを行う。
  4. 適切な DOM イベントを受け取ることにより、操作が完了するのを待つ。
  5. 結果 (リクエストオブジェクトにある) に応じた処理を行う。

データベースを開く
// データベースを開く
var request = indexedDB.open("myDatabase", 1); // データベース名とバージョンを指定
var db;

// ハンドラーの生成
// 何らかのエラーは発生した場合呼び出される
request.onerror = function(event) {
  console.log("Database error: " + event.target.errorCode);
};

// すべてが成功した場合呼び出される
request.onsuccess = function(event) {
  db = event.target.result;
  console.log("Database opened successfully");
};

// 新しいデータベースを作成したりバージョンを更新したりするときに呼び出される
request.onupgradeneeded = function(event) {
  db = event.target.result;
  var objectStore = db.createObjectStore("myObjectStore", { keyPath: "id", autoIncrement:true });
  // データベースの構造を定義
};

 onupgradeneeded イベントから正常に抜けた場合は、データベースを開くリクエストの onsuccess ハンドラーが実行されます。

データベース内に、オブジェクトストアを作成する。

 データベースを構築します。IndexdxDBはテーブルではなく、オブジェクトストアと呼ばれるデータを保存する仕組みを利用しています。

 キーと値の組で保存され、キーによって昇順になるようになっています。

 すべてのオブジェクトストアには、そのデータベース内で一意となる名前が必要です。オブジェクトストアは次の3つからキーを得ることができます。

  • 明示的に指定した値
  • キーパス
  • キージェネレーター
request.onupgradeneeded = function(event) {
  db = event.target.result;

   // オブジェクトストアの作成
  var objectStore = db.createObjectStore("myObjectStore", { keyPath: "id", autoIncrement:true });
  // データベースの構造を定義
};

 キーパスやキージェネレーターは省略できますが、省略した場合とそうではない場合で違いがあります。詳細は下記リンク先をご覧ください。

developer.mozilla.org

データの追加、読み取り、削除

 データの追加、読み取り、削除をするには、トランザクションを開始する必要があります。

 トランザクションは次の3つのモードがあります。

種類 説明
readonly 読み込み専用
readwrite 読み書き
versionchange オブジェクトストアやインデックスを作成または削除する

// トランザクションの開始
// 第1引数はトランザクションの対象にするオブジェクトストアのリスト
// 第2引数はモード
var transaction = db.transaction(["customers"], "readwrite");

(例:データを追加する関数)

function addTask() {
  // タスクのテキスト、期日の取得
  var taskInput = document.getElementById("taskInput");
  var dueDateInput = document.getElementById("dueDate");
  var task = taskInput.value;
  var dueDate = dueDateInput.value;

  // タスク、期日が入力されていない場合、アラートで知らせる
  if (task.trim() === "" || dueDate.trim() === "")
  {
    alert("テキストまたは期日が入力されていません");
    return;
  }

  // ここからデータの追加
 // オブジェクトストア"tasks"の追加をする
  var transaction = db.transaction(["tasks"], "readwrite");  // トランザクションの開始
  var objectStore = transaction.objectStore("tasks");
  var newItem = { task: task, dueDate: dueDate };  // 追加したいオブジェクトを定義
  var request = objectStore.add(newItem);  // オブジェクトの追加

  // 正常に追加できたら、呼ばれるイベントハンドラーを定義
  request.onsuccess = function(event) {
    displayTasks();
    taskInput.value = "";
    dueDateInput.value = "";
  };
}

 { task: task, dueDate: dueDate }はオブジェクトや連想配列と呼ばれるものです。

x-tech.pasona.co.jp

 追加は上のように db.transaction().objectStore().add() を用います。

 削除はadd() 関数部分を delete()に、取得はget()にすればよいです。

全削除

 オブジェクトストアを全削除する際、objectStore.clear()関数を使うことができます。

function clearTasks() {
  var transaction = db.transaction(["tasks"], "readwrite");
  var objectStore = transaction.objectStore("tasks");
  var request = objectStore.clear();
  request.onsuccess = function(event) {
    displayTasks();
  };
}
カーソル

 オブジェクトストア内のすべての値を扱いたい場合は、カーソルを使用できます。

const objectStore = db.transaction("customers").objectStore("customers");

objectStore.openCursor().onsuccess = (event) => {
  const cursor = event.target.result;
  if (cursor) {
    console.log(`Name for SSN ${cursor.key} is ${cursor.value.name}`);
    cursor.continue();
  } else {
    console.log("No more entries!");
  }
};
IndexedDBの確認

 ブラウザを開いた状態で、F12キーを押し、デベロッパーツールを開きます。Application にある Local Strage 内のファイルに保存されていることが確認できます。また、ここからデータを削除することも可能です。

IndexedDBを使ってToDOアプリを作ってみた

完成図


機能
  • タスクの文字列と期日を取得する
  • 取得したタスクと期日を表示する
  • タスクを全消去するボタンをつける
コード全文
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IndexedDB To-Do List with Due Date</title>
<style>
  body {
    font-family: Arial, sans-serif;
    margin: 20px;
    background-color: #f8f9fa;
  }
  h2 {
    color: #343a40;
  }
  input[type="text"], input[type="date"] {
    width: 250px;
    padding: 8px;
    margin-right: 10px;
    border: 1px solid #ced4da;
    border-radius: 4px;
  }
  button {
    padding: 8px 12px;
    margin-left: 5px;
    background-color: #007bff;
    color: #fff;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  }
  button:hover {
    background-color: #0056b3;
  }
  ul {
    list-style-type: none;
    padding: 0;
  }
  li {
    margin-bottom: 10px;
    background-color: #fff;
    padding: 8px;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }
</style>
</head>
<body>
<h2>IndexedDB To-Do List with Due Date</h2>
<input type="text" id="taskInput" placeholder="Add new task">
<input type="date" id="dueDate">
<button onclick="addTask()">Add Task</button>
<button onclick="clearTasks()">Clear All Tasks</button>
<ul id="taskList"></ul>

<script>
var request = indexedDB.open("todoDatabase", 1);
var db;

request.onerror = function(event) {
  console.log("Database error: " + event.target.errorCode);
};

request.onsuccess = function(event) {
  db = event.target.result;
  displayTasks();
};

request.onupgradeneeded = function(event) {
  db = event.target.result;
  var objectStore = db.createObjectStore("tasks", { keyPath: "id", autoIncrement:true });
};

function addTask() {
  var taskInput = document.getElementById("taskInput");
  var dueDateInput = document.getElementById("dueDate");
  var task = taskInput.value;
  var dueDate = dueDateInput.value;
  if (task.trim() === "" || dueDate.trim() === "")
  {
    alert("テキストまたは期日が入力されていません");
    return;
  }
  
  var transaction = db.transaction(["tasks"], "readwrite");
  var objectStore = transaction.objectStore("tasks");
  var newItem = { task: task, dueDate: dueDate };
  var request = objectStore.add(newItem);

  request.onsuccess = function(event) {
    displayTasks();
    taskInput.value = "";
    dueDateInput.value = "";
  };
}

function displayTasks() {
  var transaction = db.transaction(["tasks"], "readonly");
  var objectStore = transaction.objectStore("tasks");
  var taskList = document.getElementById("taskList");
  taskList.innerHTML = "";
  
  objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if (cursor) {
      var li = document.createElement("li");
      li.textContent = cursor.value.task + " - Due: " + cursor.value.dueDate;
      taskList.appendChild(li);
      cursor.continue();
    }
  };
}

function clearTasks() {
  var transaction = db.transaction(["tasks"], "readwrite");
  var objectStore = transaction.objectStore("tasks");
  var request = objectStore.clear();
  request.onsuccess = function(event) {
    displayTasks();
  };
}
</script>
</body>
</html>