HTML5 のデータベース機能とオフライン機能を使用する: 第 2 回 HTML5 の IndexedDB API を活用する

HTML5 は 2014年まで W3C (World Wide Web Consortium) の正式な標準にはならない見通しですが、Web ブラウザーのベンダーは既に HTML5 の機能をブラウザーに追加したり、宣伝したりしています。その HTML5 の機能のなかでも、オフライン・アプリケーションのサポート機能とローカル永続ストレージ機能を利用すると、オンラインでもオフラインでも同じリッチなユーザー・エクスペリエンスを実現することができます。こうしたことは、以前は専用のデスクトップ・アプリケーション開発フレームワークを使用しない限り実現できなかったことです。この記事では第 1 回で説明した内容を基に、データをローカルに永続化するオフライン・アプリケーションを、IndexedDB (Indexed Database) API を利用して作成する方法について説明します。

Brian J Stewart, Principal Consultant, Aqua Data Technologies, Inc.

Photo of Brian StewartBrian J. Stewart は、Aqua Data Technologies で主任コンサルタントを務めています。彼が設立したこの会社は、コンテンツ・マネージメント、XML 技術、そしてエンタープライズ・クライアント/サーバーおよび Web システムを専門としています。彼は Java EE および Microsoft .NET プラットフォームをベースとしてエンタープライズ・ソリューションを設計、開発しています。



2012年 12月 20日

「HTML5 のデータベース機能とオフライン機能を使用する: 第 1 回」では、HTML5 仕様で利用できるようになったオフライン・アプリケーションのサポート機能機能とローカル・データ永続化機能について紹介し、localStorage に焦点を当てました。今回は HTML5 標準の一部を構成する堅牢なデータ永続化技術、IndexedDB (Indexed Database) について紹介し、IndexedDB データ・プロバイダーを第 1 回の記事で作成した Contact Manager アプリケーションと統合する方法について説明します。

この (氏名、住所、電話番号用の) Contact Manager サンプル・アプリケーションには、オンライン・モードとオフライン・モードがあります。オフラインの場合、データはローカルの永続ストレージに保存されます。オンライン・モードに切り換えると、単純なデータ同期機能により、ローカルで行われたデータの変更はサーバーと同期されます。このアプリケーションは永続ストレージのための 4 つの基本機能、つまり CRUD (Create, Read, Update, Delete) をオンライン・モードでもオフライン・モードでもサポートしています。

アプリケーションのアーキテクチャーの概要

サーバー・インターフェース

サーバー・インターフェースは、連絡先サーブレット (ContactServlet) と辞書サーブレット (DictionaryServlet) という 2 つのサーブレットで構成されています。このインターフェースの概要は、第 1 回の表を参照してください。サーブレットの実装と、それに対応するビジネス・サービスおよびデータ・プロバイダーの実装については、この記事では説明しません。

まずは復習のため、Contact Manager アプリケーションのアーキテクチャーを示す図 1 を見てください。サーバー・アーキテクチャーはビジネス・サービスとデータ・プロバイダーに対応する 2 つのサーブレットで構成されています。UI は 1 つの HTML ファイルと 4 つの JavaScript モジュール、そして最新バージョンの jQuery ライブラリーへの外部参照で構成されています。

この記事では、オフラインのデータベース・プロバイダーを IndexedDB API ベースのデータベース・プロバイダーで置き換えます。具体的には localdb.js という JavaScript モジュールを置き換えます。

図 1. Contact Manager アプリケーションのアーキテクチャー
この画像はアプリケーションのアーキテクチャーを示しており、クライアント・アーキテクチャーとサーバー・アーキテクチャーの下に、さまざまなサーブレットを示すボックスが表示されています。

データ・モデルの概要

データ・モデルは contact と state という 2 つのデータ・エンティティーで構成されています (図 2 を参照)。contact テーブルには実際の連絡先データが含まれ、state テーブルには状態選択リスト用の辞書の値が含まれています。

図 2. Contact Manager アプリケーションのデータ・モデル
データ・モデルを示す画像

IndexedDB API

HTML5 仕様には永続ストレージ技術がいくつか含まれています。IndexedDB は HTML5 対応のブラウザーに推奨されるデータベースであり、非推奨となっている WebSQL データベースを置き換えるものです。

IndexedDB API のサポートおよび実装は、すべての Web ブラウザーで等しく行われているわけではありません。この記事の執筆時点で IndexedDB をサポートしているのは、Google Chrome 11 またはそれ以降のバージョン、Mozilla Firefox 4 またはそれ以降のバージョン、Windows Internet Explorer 10 です。

Web サイトは一意の名前で識別されるデータベースを 1 つまたは複数持つことができます。各データベースは 1 つまたは複数の「オブジェクト・ストア」を持つことができます。オブジェクト・ストアは、一意の名前で識別され、レコードの集合であるという点で、リレーショナル・データベースのテーブルと似ています。その一方で、データの保管、データへのアクセス、そしてデータに対するクエリーの方法は、リレーショナル・データベースのテーブルとは異なります。オブジェクト・ストア内のデータはキーと値によって格納されます。キーは 1 つのオブジェクト・ストア内では一意でなければならず、キー・ジェネレーターによって指定、つまり生成することができます。

IndexedDB API の仕様には、データベースの一般的な構成体 (トランザクション、インデックスの作成、データのクエリー、カーソルなど) のサポートが含まれている他、同期型 API と非同期型 API の両方があります。同期型 API は Web ワーカー内で使用するためのものです。ただし、すべての Web ブラウザーが Web ワーカーと IndexedDB の同期型 API をサポートしているわけではありません。非同期型 API はリクエストとコールバックを使用します。すべてのデータベース操作 (データベースのオープン、データの取得、データの照会、データの削除など) には、リクエスト API を呼び出す方法が用意されています。各リクエストには、操作が成功した場合に呼び出される onsuccess コールバックと、失敗した場合に呼び出される onerror コールバックがあります。これらのコールバックは、リクエスト API の呼び出し元に返すデータ用にイベントの結果のパラメーターを提供します。

サンプル・アプリケーション Contact Manager は 2 つのオブジェクト・ストアを持つ 1 つのデータベースで構成されます。オブジェクト・ストアの 1 つは実際の連絡先のレコードを含む「連絡先 (contacts)」オブジェクト・ストアです。もう 1 つのオブジェクト・ストアは状態選択リストの値を含む「状態 (states)」オブジェクト・ストアです。この記事では非同期型の API を使用してデータにアクセスする方法について説明します。


データベースへの接続と、データベースとの接続の切断

IndexedDB の実装は Web ブラウザーごとに少しずつ異なるため、これに対応するには、グローバル変数 (localDatabase) を作成し、その変数を Web ブラウザーの実装に応じて初期化するのが最善です。このグローバル変数によって IndexedDB API を参照します。

localDatabase 変数を作成したら、次のステップは open メソッドを使用してデータベースを開くことです。データベースを開くためのリクエストが成功すると、onsuccess コールバックが呼び出されます。データベース操作のためのコードはすべて onsuccess コールバックの中で実行される必要があります。データベースを開こうとしてエラーが発生した場合には onerror コールバックが呼び出されます。リスト 1 はデータベースを初期化して開くためのコードを示しています。

リスト 1. データベースを開く
var localDatabase = {};

localDatabase.indexedDB = window.indexedDB || window.mozIndexedDB || 
   window.webkitIndexedDB || window.msIndexedDB;
localDatabase.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
localDatabase.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;

console.log('opening local database');
var openRequest = localDatabase.indexedDB.open(dbName);
openRequest.onerror = function(e) {
   console.log("Database error: " + e.target.errorCode);
};
openRequest.onsuccess = function(event) {
   console.log("open database request succeeded ");

   console.log('set db');
   db = openRequest.result;

...
};

データベースとの接続を切断するには、単純に IndexedDB データベース・オブジェクトの close メソッドを呼び出します。


オブジェクト・ストアを作成する

次のステップは、データベースの中にオブジェクト・ストアを作成することです。オブジェクト・ストアはいつでも作成することができますが、オブジェクト・ストアを作成した後は、データベースを開くリクエストの onupgradeneeded コールバックの中以外ではオブジェクト・ストアを変更することはできません。このコールバックはデータベースの更新が必要なことを示しています。

リスト 2 のコードは 2 つのオブジェクト・ストアを作成する方法を示しています。

リスト 2. オブジェクト・ストアを作成する
openRequest.onupgradeneeded = function (evt) {   
console.log('creating object stores');
var contactsStore = evt.currentTarget.result.createObjectStore
(contactStore, {keyPath: "id"});
var statesStore = evt.currentTarget.result.createObjectStore
(stateStore, {keyPath: "itemId"});
console.log('object stores created');
};

keyPath によってオブジェクト・ストアのキー・フィールドが識別されます。


選択リストの辞書を作成する

オブジェクト・ストアの中のレコードはトランザクションを使用して作成、変更されます。IndexedDB API にはトランザクションのモードとして以下の 3 つが用意されています。

  • readonly — オブジェクト・ストアに読み取り専用でアクセスすることができます。
  • readwrite — オブジェクト・ストアに読み書きアクセスすることができます。
  • versionchange — オブジェクト・ストアに読み書きアクセスできる他、オブジェクト・ストアを作成、削除することができます。

トランザクションを作成するには以下の構文を使用します。

var transaction = db.transaction("states", "readwrite");

トランザクションを作成できると、次のステップはオブジェクト・ストアに対する参照を取得することです。transaction オブジェクトには、データベースに関連付けられたオブジェクト・ストアにアクセスするための objectstores プロパティーが含まれています。states オブジェクト・ストアに対する参照を取得するには以下のステートメントを使用します。

var store = transaction.objectStore("states");

サーバーに接続したら、ローカルの states データをすべてサーバーの最新データで置き換える必要があります。そのためには、states オブジェクト・ストアの clear メソッドを使用して states オブジェクト・ストアをクリアしてから、サーバー上にある最新の値を格納する必要があります。

オブジェクト・ストアのクリアに成功すると、onsuccess コールバックが呼び出されます。このコールバックの中では、ループ処理によってローカルの states オブジェクト・ストアに各値を追加することができます。オブジェクト・ストアに追加するデータはストリング配列 stateArray に含まれています。この配列に対し、jQuery の $.each メソッドを使用して繰り返し処理を行います。states 配列の各値に対して put メソッドを呼び出し、states オブジェクト・ストアにレコードを追加します。

リスト 3 のコード・フラグメントは、オブジェクト・ストアに states 配列の値を追加してローカルでもオンラインでもアクセスできるようにする方法を示しています。

リスト 3. 状態データをオブジェクト・ストアに保存する
try {
   console.log('saving local state data');

   var openRequest = localDatabase.indexedDB.open(dbName);

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

   openRequest.onsuccess = function(event) {
      db = openRequest.result;

      console.log('opening states store');
      var transaction = db.transaction("states", "readwrite");
      var store = transaction.objectStore("states");                    
     
      var clearReq = store.clear();
      clearReq.onsuccess = function (ev) { 
         console.log('cleared state store');
      
         $.each(stateArray, function(i,item){
            var itemId = generateUUID();
   
            var request = store.put({
               "text": item,
               "itemId" : itemId
            });
   
            request.onsuccess = function(e) {
            };
            
            request.onerror = function(e) {
               console.log(e.value);
            };
         });
      };

      db.close();
   };
}
catch(e){
   console.log(e);
}

次は、オフラインの状態で連絡先のレコードを追加、更新する方法について説明します。


連絡先の追加や更新を行う

この記事では、第 1 回で (オフライン・データを localstorage に格納した場合に) 使用したのと同じ方法でレコードを作成、更新します。新しいレコードは ID フィールドに対して一意に生成される負の数値で識別されます。この負の数値は、このレコードが新規であること、そしてこのレコードをサーバーのデータベース・テーブルの中に作成する必要があることを示します。また isDirty フラグは、このレコードがオフラインの間に変更または作成されたことを示します。(states オブジェクト・ストアに値を追加したのと同様の方法で) put メソッドを使用して contacts オブジェクト・ストアにレコードを保存します。リスト 4 はオブジェクト・ストアに 1 つのレコードを作成する場合と更新する場合の完全なコードを示しています。

リスト 4. 連絡先のレコードの作成および更新を行う
openRequest.onsuccess = function(event) {
   db = openRequest.result;
   
   var transaction = db.transaction(contactStore, "readwrite");
   var objectStore = transaction.objectStore(contactStore);
    
   var id = $('#contactId').val();
   var firstName = $('#firstName').val();
   var lastName = $('#lastName').val();
   var street1 = $('#street1').val();
   var street2 = $('#street2').val();
   var city = $('#city').val();
   var zipCode= $('#zipCode').val();
   var state= $('#state').val();;
   
   if (contactId > 0) {
      var getRequest = objectStore.get(parseInt(contactId));
      
      getRequest.onsuccess = function(event)
      {  
         var contact = event.target.result;

         contact.firstName = firstName;
         contact.lastName = lastName;
         contact.street1 = street1;
         contact.street2 = street2;
         contact.city = city;
         contact.zipCode = zipCode;
         contact.isDirty = true;
         contact.lastModifyDate = "";
         contact.isDeleted = false;
         
         var addRequest = objectStore.put(contact);
         
         addRequest.onsuccess = function(event) {
            recordUpdated=true;
         };
   
         addRequest.onerror = function(e) {
            console.log(e.value);
         };
      };
      
      getRequest.onerror = function(e) {
         console.log(e.value);
      };
   } // if update
   else {
      var newContactId = (-1) * Math.floor(Math.random()*100000);

      var lastModifyDate = "";
      var newContact = {
         "timeStamp": "", 
         "id":newContactId,
         "firstName": firstName,
         "lastName": lastName,
         "street1": street1,
         "street2": street2,
         "city": city,
         "zipCode": zipCode,
         "state": state,
         "isDirty":true,
         "lastModifyDate": "",
         "isDeleted":false };
      
      var request = objectStore.put(newContact);
      
      var nextIndex = data.length;
      data[nextIndex] = newContact;
      recordUpdated=true;
   } // if create

次は、オフラインの状態で連絡先のレコードを削除する方法について説明します。


オフラインの状態で連絡先のレコードを削除する

オフラインの状態で連絡先を削除する場合、そのレコードを実際に削除するのではなく、オフラインでの連絡先リストにそのレコードが表示されないようにすることで、そのレコードが削除されたことを示す必要があります。そのためには (この連載の第 1 回で説明した方法と同じように) isDeleted フラグを使用します。isDirty フラグも true にセットし、オフラインの間にそのレコードが変更されたことを示します。そして、サーバーのデータベース内にあるオブジェクト・ストアのレコードが変更されたら、連絡先リストを更新し、そのレコードを連絡先リストから削除します (isDeleted フラグが true にセットされているレコードは表示されません)。そのためのコードをリスト 5 に示します。

リスト 5. 連絡先のレコードを削除する
try {
   console.log('deleting local contact');
   
   var openRequest = localDatabase.indexedDB.open(dbName);
   console.log('after open');
   openRequest.onerror = function(e) {
      console.log("Database error: " + e.target.errorCode);
   };
   openRequest.onsuccess = function(event) {
      db = openRequest.result;
   
      console.log('opening contacts store');
      var transaction = db.transaction(contactStore, "readwrite");
      var store = transaction.objectStore(contactStore);

      var getRequest = store.get(contactId);  
      getRequest.onsuccess = function (ev) {
         var item = getRequest.result;
         item.isDeleted=true;
         item.isDirty=true;

         var request = store.put(item);

         request.onsuccess = function(e) {
            alert('Contact deleted');
            loadOfflineContacts();
         };

         request.onerror = function(e) {
            console.log(e.value);
         };
      }

      getRequest.onerror = function(e) {
         console.log(e.value);
      };

      db.close();
   };              

   loadOfflineContacts();
}
catch(e){
   console.log(e);
}

連絡先に対するクエリーを実行する

contacts オブジェクト・ストアから連絡先のレコードを取得するには、IndexedDB のカーソルを使用します。リレーショナル・データベースのデータベース・カーソルと同じように、IndexedDB のカーソルを使用すると、オブジェクト・ストア内のレコードに対して繰り返し処理を行うことができます。レコードを繰り返し処理する場合には、連絡先レコードを含む配列を作成します。isDeleted フラグが true にセットされているレコードはすべて無視されます。リスト 6 は contacts オブジェクト・ストアに含まれているデータを使用して連絡先の配列を作成する方法を示しています。

リスト 6. 連絡先に対するクエリーを実行する
var data = new Array();
...
var cursorRequest = objectStore.openCursor();
cursorRequest.onsuccess = function(evt) {  
   var cursor = evt.target.result;  
   if (cursor) {  
      if (!cursor.value.isDeleted) {
         var newContact = {
            "timeStamp":cursor.value.timeStamp,
            "id":cursor.value.id,
            "firstName": cursor.value.firstName,
            "lastName": cursor.value.lastName,
            "street1": cursor.value.street1,
            "street2": cursor.value.street2,
            "city": cursor.value.city,
            "zipCode": cursor.value.zipCode,
            "state": cursor.value.state,
            "lastModifyDate": cursor.value.lastModifyDate,
            "isDeleted": cursor.value.isDeleted,
            "isDirty": cursor.value.isDirty
         };
         //console.log('adding ' + newContact.toString());
         //console.log("adding contact to array: " + data.length);
         data[data.length]= newContact;
      }
   
      cursor.continue();
   } // more records
   else {
      displayContactData(data);    
   } // no more records
};  // open cursor

次は、オフラインで追加されたデータや変更されたデータをサーバーと同期させるための単純なアルゴリズムと手法について説明します。


サーバーとローカル・データとを同期させる

オンラインの状態で作業を行っているときには、すべての CRUD 操作でサーブレットが使用され、サーバーのデータベースは瞬時に更新されます。オンラインの状態でもオフラインの状態でも常に最新データを利用できるように、ローカルの (IndexedDB) データベースでは、オンラインでデータベースに変更が加えられた場合でも、その変更による更新が行われます。

オフラインの状態のときには、どの CRUD 操作を実行した場合も IndexedDB データベースのデータが更新されます。再度サーバーと接続されると、以下の内容が実行されます。

  • ローカル・データベースで作成されたレコードはすべてサーバーに永続化されます。
  • ローカル・データベースで更新されたレコードはすべてサーバーで更新されます。
  • ローカル・データベースで削除されたレコードはすべてサーバーから削除されます。

リスト 7 のコードは同期の完全な方法を示しています。第 1 回で説明したオンライン関数と同じ関数を使用して作成、更新、削除の操作を行います。

最初のステップでは、contacts オブジェクト・ストア内のレコードに対してカーソルを使用して繰り返し処理を行い、サーバーに送信する必要のあるレコードがすべて含まれた配列を作成します。ローカルで更新または作成されたレコードは isDirty プロパティーが true にセットされています。保存操作の対象となるレコードの一意の ID が負の場合 (つまり MySQL データベースによって ID が割り当てられていない場合) には、その保存操作は新規レコードに対する操作とみなされます。ローカルで削除されたレコードは isDeleted プロパティーを使用してフラグが立てられます。

サーバーに送信する必要のあるレコードすべてを含む配列を用意できたら、jQuery の $.each メソッドを使用してその配列に対して繰り返し処理を実行し、それぞれの変更をサーバーに送信します。

リスト 7 に示すデータ同期の方法が完了すると、最後にサーバーの最新データでローカルの contacts オブジェクト・ストアが更新されます。この最新データには、オフラインの間に皆さん (そして他のユーザー) が行ったすべての変更が含まれています。

リスト 7. サーバーとローカル・データとを同期させる
...
cursorRequest.onsuccess = function(evt) {  
var cursor = evt.target.result;  

if (cursor) {  
   var isDirty = cursor.value.isDirty;
   var curId = cursor.value.id;
   console.log(curId + ' isDirty = ' + isDirty);
   if (isDirty) {
      var newContact = {
         "timeStamp":cursor.value.timeStamp,
         "id":curId,
         "firstName": cursor.value.firstName,
         "lastName": cursor.value.lastName,
         "street1": cursor.value.street1,
         "street2": cursor.value.street2,
         "city": cursor.value.city,
         "zipCode": cursor.value.zipCode,
         "state": cursor.value.state,
         "lastModifyDate": cursor.value.lastModifyDate,
         "isDeleted": cursor.value.isDeleted,
         "isDirty": cursor.value.isDirty
      };
      //console.log('adding ' + newContact.toString());
      //console.log("adding contact to array: " + data.length);
      data[data.length]= newContact;
   }

   cursor.continue();
} // more records
else {
   console.log("no more records");

   console.log('number of modified records: ' + data.length);
   var recordsUpdated = 0;
   var recordsCreated = 0;
   var recordsDeleted = 0;
   
   $.each(data, function(i,item){
      console.log("processing record " + item.id);
      if (item.isDeleted) {
         deleteOnlineContact(item.id, true);
         recordsDeleted++;
      }
      else if (item.isDirty && !item.isDeleted) {
         $('input[name="contactId"]')[0].value = item.id;
         $('input[name="firstName"]')[0].value = item.firstName;
         $('input[name="lastName"]')[0].value = item.lastName;
         $('input[name="street1"]')[0].value = item.street1;
         $('input[name="street2"]')[0].value = item.street2;
         $('input[name="city"]')[0].value = item.city;
         $('select[name="state"]')[0].value = item.state;
         $('input[name="zipCode"]')[0].value = item.zipCode;

         var dataString = $("#editContactForm").serialize();
         postEditedContact(dataString, true);
         if (item.id > 0) {
            recordsUpdated++;
         }
         else {
            recordsCreated++;
         }   
      }
   });

   var msg = "Synchronization Summary\n\tRecords Updated: " + recordsUpdated 
      + "\n\tRecords Created: " + recordsCreated 
      + "\n\tRecords Deleted: " + recordsDeleted;
   alert(msg);

まとめ

この記事では、第 1 回で説明した内容を基に、オンラインのサポートとオフラインのサポートの両方に同じパターンを使用して、一貫性のあるユーザー・エクスペリエンスを維持できるようにしました。また、IndexedDB API と、オフラインで作成、削除、変更されたレコードの同期処理を行うデータ同期アルゴリズムを紹介しました。この記事で紹介したコードには、堅牢なエラー処理機能や競合 (ローカルでの変更と、別のユーザーによるサーバー上での変更が同じレコードに対して行われた場合) の解決機能が含まれていないため、このコードを本番で使用することはできませんが、今後の作業にふさわしい基礎を提供しています。

参考文献

学ぶために

  • HTML5 の基礎: 第 1 回 最初の一歩」(developerWorks、2011年6月): この 4 回連載の記事を読み、HTML5 で行われた変更について学んでください。
  • HTML5、CSS3、および関連技術」(developerWorks、2011年6月): HTML5 仕様に含まれている内容、それらが CSS3 に与える影響について学んでください。
  • HTML5 の基礎」: このナレッジ・パスで HTML5 について学んでください。
  • Web ワーカー: この API の最新資料を見てください。この API を利用すると、メイン・ページに対して並列にスクリプトを実行するバックグラウンド・ワーカーを作成することができます。
  • Web ストレージ: この API の資料を見てください。この API を利用すると、キーと値のペアのデータを Web クライアントに永続化することができます。
  • developerWorks の Web development ゾーン: Web ベースのさまざまなソリューションを解説した記事が豊富に用意されています。Web development 技術文書一覧に用意された、さまざまな技術記事やヒント、チュートリアル、技術標準、IBM Redbooks をご覧ください。

製品や技術を入手するために

  • IBM 製品の評価版: IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2、Lotus、Rational、Tivoli、WebSphere などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

議論するために

  • developerWorks コミュニティー: ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、ウィキを調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=852572
ArticleTitle=HTML5 のデータベース機能とオフライン機能を使用する: 第 2 回 HTML5 の IndexedDB API を活用する
publish-date=12202012