HTML 5 を利用した Web アプリケーションを作成する

明日の Web アプリケーションを今日作成する

HTML 5 の仕様に概要が示された、次世代 Web ブラウザーに約束される機能のなかには、Web 開発者達が長年熱望していた機能があります。そうした機能のうち、どれほど多くのものが現在あるブラウザーで既に利用できるかを知ると、皆さんは驚くかもしれません。この記事では、どの機能がブラウザーの中にあるのかを検出する方法、そしてそうした機能をアプリケーションの中で活用する方法について学びます。マルチスレッド処理、ジオロケーション、組み込みデータベース、埋め込み動画など、HTML 5 の強力な機能について詳しく見ていきましょう。

Michael Galpin, Software architect, eBay

Michael Galpin's photoMichael Galpin は eBay のアーキテクトであり、developerWorks に頻繁に寄稿しています。彼は JavaOne、EclipseCon、AjaxWorld など、さまざまな技術カンファレンスで講演を行っています。彼が次に取り組もうとしていることを知るには、Twitter で @michaelg をフォローしてください。



2010年 3月 30日

はじめに

HTML 5 では、多くの新しい機能や標準が生まれています。今日のブラウザーで利用可能な機能を検出できると、そうした機能をアプリケーションの中で活用することができます。この記事ではサンプル・アプリケーションを作成しながら、最新の Web 技術を検出して使用する方法を学びます。この記事で紹介するコードの大半は、すべての Web 開発者にとっての中核技術、つまり HTML、JavaScript、そして CSS が使われています。

準備するもの

この記事のサンプルを理解する上で最も重要なことは、テスト用に複数のブラウザーを用意することです。最新バージョンの Mozilla Firefox、Apple Safari、Google Chrome を強く推奨します。この記事では Mozilla Firefox 3.6、Apple Safari 4.04、Google Chrome 5.0.322 を使用しました。また、モバイル・ブラウザーでもテストする必要があるかもしれません。例えば、この記事では最新の Android SDKと iPhone SDK を使用して、両者のエミュレーター上で 2 つのブラウザーをテストしました。

この記事で使用したソース・コードはダウンロードすることができます。

この記事で紹介するサンプルには、Java™ で作成された非常に簡単なバックエンド・コンポーネントが含まれています。ここでは JDK 1.6.0_17 と Apache Tomcat 6.0.14 を使用しました。これらのツールをダウンロードするためのリンクは「参考文献」を参照してください。


機能を検出する

Web 開発者はコードの作成に 20% の時間を費やし、残りの 80% は同じコードをすべてのブラウザーで動作させるために費やす、という言い古された冗談があります。非常に控えめな言い方をすれば、Web 開発者はブラウザー間の違いへの対応に慣れている、と言うこともできます。ブラウザー革新の新たな波が押し寄せる中で、最新、最強のブラウザーでサポートされる機能は常に変化しているため、またもこのやりきれない対応をせざるを得ないようです。

ただし幸いなことに、新しい一連の機能が Web 標準として収束しつつあります。そのため、これらの新機能を今から使い始めることができます。つまり、Progressive Enhancement という古い手法を使用してベースとなる機能を提供し、新機能があるかどうかを調べ、新機能がある場合にはそれらによってアプリケーションを強化することができます。それを踏まえ、新機能のいくつかを検出する方法について調べましょう。リスト 1 は簡単な検出スクリプトを示しています。

リスト 1. 検出スクリプト
function detectBrowserCapabilities(){
    $("userAgent").innerHTML = navigator.userAgent;    
    var hasWebWorkers = !!window.Worker;
    $("workersFlag").innerHTML = "" + hasWebWorkers;
    var hasGeolocation = !!navigator.geolocation;
    $("geoFlag").innerHTML = "" + hasGeolocation;
    if (hasGeolocation){
        document.styleSheets[0].cssRules[1].style.display = "block";
        navigator.geolocation.getCurrentPosition(function(location) {
            $("geoLat").innerHTML = location.coords.latitude;
            $("geoLong").innerHTML = location.coords.longitude;
        });
    }
    var hasDb = !!window.openDatabase;
    $("dbFlag").innerHTML = "" + hasDb;
    var videoElement = document.createElement("video");
    var hasVideo = !!videoElement["canPlayType"];
    var ogg = false;
    var h264 = false;
    if (hasVideo) {
        ogg = videoElement.canPlayType('video/ogg; codecs="theora, vorbis"') || "no";
           h264 = videoElement.canPlayType('video/mp4; 
codecs="avc1.42E01E, mp4a.40.2"') || "no";
    }
    $("videoFlag").innerHTML = "" + hasVideo;
    if (hasVideo){
        var vStyle = document.styleSheets[0].cssRules[0].style;
        vStyle.display = "block";
    }
    $("h264Flag").innerHTML = "" + h264;
    $("oggFlag").innerHTML = "" + ogg;
}

HTML 5 標準の一部として、膨大な数の新機能や新しい標準が生まれています。この記事では、比較的有用ないくつかの機能のみに焦点を絞ります。リスト 1 のスクリプトは下記の 4 つの機能を検出します。

  • Web ワーカー (マルチスレッド処理)
  • ジオロケーション
  • データベース・ストレージ
  • ネイティブ動画再生

このスクリプトは最初に、ユーザーのブラウザーのユーザー・エージェントを表示します。ユーザー・エージェントは (通常は) ブラウザーの種類を識別するためのストリングです。このストリングは簡単に偽装できますが、このアプリケーションでは、このストリングを単純に出力すれば十分です。次のステップで機能の検出を始めます。まず、グローバル・スコープ (window) で Worker 関数を検索し、Web ワーカーがあるかどうかを調べます。ここではイディオムのような JavaScript、つまり二重否定を使っています。Worker 関数が存在しない場合には window.Worker は未定義と評価されますが、未定義は JavaScript では「偽」の値になります。この値の前に否定を 1 つ置くと真と評価されます。従って二重否定は偽と評価されます。この値をテストした後、スクリプトではリスト 2 の DOM 構造を変更することで評価結果を画面に出力します。

リスト 2. 検出用の DOM
<input type="button" value="Begin detection" 
    onclick="detectBrowserCapabilities()"/>
<div>Your browser's user-agent: <span id="userAgent">
   </span></div>
<div>Web Workers? <span id="workersFlag"></span></div>

<div>Database? <span id="dbFlag"></span></div>
<div>Video? <span id="videoFlag"></span></div>
<div class="videoTypes">Can play H.264? <span id="h264Flag">
   </span></div>
<div class="videoTypes">Can play OGG? <span id="oggFlag">
   </span></div>
<div>Geolocation? <span id="geoFlag"></span></div>
<div class="location">
    <div>Latitude: <span id="geoLat"></span></div>
    <div>Longitude: <span id="geoLong"></span></div>
</div>

リスト 2 は、検出スクリプトによって収集される診断情報を表示するための単純な HTML 構造です。リスト 1 に示すように、次はジオロケーションをテストします。ここでも二重否定の手法を使用しますが、この場合は geolocation というオブジェクトがあるかどうかをチェックします。geolocation オブジェクトは navigator オブジェクトのプロパティーとして存在しているはずです。geolocation オブジェクトが存在する場合には、geolocation オブジェクトの getCurrentPosition 関数を使って現在の位置を取得します。位置情報の取得には時間がかかる場合がありますが、これは Wi-Fi ネットワークをスキャンする必要があるためです。モバイル機器の場合には、さらに基地局のスキャンや GPS 衛星への ping も行われるかもしれません。getCurrentPosition 関数は実行を完了するまでに時間がかかるため非同期であり、コールバック関数を引数に取ります。この場合にはコールバック関数にクロージャーを使用します。クロージャーは (geolocation オブジェクトの CSS を切り換えることで) 単純に location フィールドを表示し、緯度と経度を DOM に書き込みます。

次のステップでは、データベース・ストレージがあるかどうかをチェックします。そのために、クライアント・サイドのデータベースの作成とアクセスに使用されるグローバル関数 openDatabase があるかどうかをチェックします。

最後に、ネイティブ動画再生機能があるかどうかをチェックします。DOM API を使用して video 要素を作成します。現在では、すべてのブラウザーが video 要素を作成できるはずです。古いブラウザーの場合、video 要素は有効な DOM 要素ですが、何も特別な意味を持たないため、video 要素の作成は foo という要素を作成する場合と似ています。最近のブラウザーでは video 要素は特別な要素であるため、video 要素の作成は div 要素や table 要素を作成するのと同様です。video 要素は canPlayType という関数を持つので、この関数があるかどうかを単純にチェックします。

ブラウザーがネイティブ動画再生機能を持っていたとしても、そのブラウザーで再生できる動画のタイプ、つまりサポートされるコーデックは標準化されていません。そのため、ブラウザーでサポートされるコーデックをチェックする必要があります。コーデックの標準リストというものはありませんが、2 つの最も一般的なコーデックは H.264 と Ogg Vorbis です。特定のコーデックをサポートしているかどうかをチェックするためには、そのコーデックを識別するストリングを canPlayType 関数に渡します。そのコーデックをブラウザーがサポートしている場合には、この関数は probably という値を返します (実際に probably が返されます)。サポートしていない場合には canPlayType 関数がヌルを返します。この検出コードでは単純にこれらの値をチェックし、その答えを DOM の中に表示します。いくつかの一般的なブラウザーでこれらのコードをテストし、その結果をまとめたものがリスト 3 です。

リスト 3. さまざまなブラウザーが持つ機能
#Firefox 3.6
Your browser's user-agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; 
en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6
Web Workers? true
Database? false
Video? true
Can play H.264? no
Can play OGG? probably
Geolocation? true
Latitude: 37.2502812
Longitude: -121.9059866

#Safari 4.0.4
Your browser's user-agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; 
en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10
Web Workers? true
Database? true
Video? true
Can play H.264? probably
Can play OGG? no
Geolocation? false

#Chrome 5.0.322
Your browser's user-agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; 
en-US) AppleWebKit/533.1 (KHTML, like Gecko) Chrome/5.0.322.2 Safari/533.1
Web Workers? true
Database? true
Video? true
Can play H.264? no
Can play OGG? no
Geolocation? false

上に挙げた一般的なデスクトップ・ブラウザーは、いずれも非常に多くの機能をサポートしています。

  • Firefox はデータベースを除くすべての機能をサポートしています。Firefox は動画に関しては Ogg のみをサポートしています。
  • Safari はジオロケーションを除くすべての機能をサポートしています。
  • Chrome はジオロケーションを除くすべての機能をサポートしていますが、H.264 も Ogg もサポートしていないという結果が出ています。これはおそらく、このテストに使用した Chrome のビルドに伴うバグまたはテスト・コードのバグです。Chrome は実際には H.264 をサポートしています。

ジオロケーションはデスクトップ・ブラウザーではあまりサポートされていませんが、モバイル・ブラウザーでは広くサポートされています。リスト 4 はモバイル・ブラウザーに対する結果をまとめたものです。

リスト 4. モバイル・ブラウザー
#iPhone 3.1.3 Simulator
Your browser's user-agent: Mozilla/5.0 (iPhone Simulator; U; CPU iPhone OS 3.1.3 
like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) 
Version/4.0 Mobile/7E18 Safari/528.16
Web Workers? false
Database? true
Video? true
Can play H.264? maybe
Can play OGG? no
Geolocation? true
Latitude: 37.331689
Longitude: -122.030731

#Android 1.6 Emulator
Your browser's user-agent: Mozilla/5.0 (Linux; Android 1.6; en-us; 
sdk Build/Donut) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 
Mobile Safari/525.20.1
Web Workers? false
Database? false
Video? false
Geolocation? false

#Android 2.1 Emulator
Your browser's user-agent: Mozilla/5.0 (Linux; U; Android 2.1; en-us;
sdk Build/ERD79) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0
Mobile Safari/530.17 
Web Workers? true 
Database? true 
Video? true 
Can play H.264? no 
Can play OGG? no 
Geolocation? true 
Latitude: 
Longitude:

上記では、最新の iPhone シミュレーターと 2 種類の Android を示してあります。Android 1.6 は今回テストした機能をどれもサポートしていません。実際には Android 1.6 は (動画を除き) これらの機能をすべてサポートしていますが、Google Gears を使ってサポートしています。Google Gears は (機能に関して) HTML 5 と等価な API ですが、Web 標準には準拠していません。そのためリスト 4 に示す結果となっています。これを Android 2.1 と比較してください。Android 2.1 はすべてをサポートしています。

iPhone は Web ワーカーを除くほとんどすべてをサポートしていることに注意してください。リスト 3 を見るとデスクトップ版の Safari は Web ワーカーをサポートしていることがわかります。そのため、この機能はまもなく iPhone にも登場するものと期待してよさそうです。

これでユーザーのブラウザーの機能の検出方法がわかったので、(ユーザーのブラウザーで何を処理できるかに応じて) これらの機能をいくつか組み合わせて使用する簡単なアプリケーションを試してみましょう。ここでは、ユーザーのいる場所の近くにある人気のスポットを Foursquare API を使用して検索するアプリケーションを作成します。


明日のアプリケーションを作成する

この例ではモバイル機器でのジオロケーションの使い方に焦点を絞りますが、Firefox 3.5 またはそれ以上もジオロケーションをサポートしていることを忘れないでください。このアプリケーションはまず、ユーザーが現在いる場所の近くに Foursquare で venue (スポット) と呼ばれるものがあるかどうかを検索します。venue はどんなものでも構いませんが、通常はレストランやバー、お店などです。このサンプル・アプリケーションは Web アプリケーションなので、すべてのブラウザーによって強制される同一生成元ポリシーによる制約を受けるため、Foursquare の API を直接呼び出すことはできません。ここでは Java サーブレットを使用することで、実質的に Foursquare の API を呼び出すためのプロキシー処理を行います。ここで Java を使用することに特別な理由があるわけではありません。同様のプロキシーを PHP、Python、Ruby 等々でも容易に作成することができます。リスト 5 はプロキシー・サーブレットを示しています。

リスト 5. Foursquare 用のプロキシー・サーブレット
public class FutureWebServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, 
            HttpServletResponse response) throws ServletException, IOException {
        String operation = request.getParameter("operation");
        if (operation != null && operation.equalsIgnoreCase("getDetails")){

            getDetails(request,response);
        }
        String geoLat = request.getParameter("geoLat");
        String geoLong = request.getParameter("geoLong");
        String baseUrl = "http://api.foursquare.com/v1/venues.json?";
        String urlStr = baseUrl + "geolat=" + geoLat + "&geolong=" + geoLong;
        PrintWriter out = response.getWriter();
        proxyRequest(urlStr, out);        
    }

    private void proxyRequest(String urlStr, PrintWriter out) throws IOException{
        try {
            URL url = new URL(urlStr);
            InputStream stream = url.openStream();
            BufferedReader reader = new BufferedReader( new InputStreamReader(stream));
            String line = "";
            while (line != null){
                line = reader.readLine();
                if (line != null){
                    out.append(line);
                }
            }
            out.flush();
            stream.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
    
    private void getDetails(HttpServletRequest request, HttpServletResponse response)
throws IOException{
        String venueId = request.getParameter("venueId");
        String urlStr = "http://api.foursquare.com/v1/venue.json?vid="+venueId;
        proxyRequest(urlStr, response.getWriter());
    }
}

ここで注意すべき重要な点は、2 つの Foursquare API をプロキシー処理している点です。1 つは検索用であり、もう 1 つは venue の詳細の取得用です。この 2 つを区別するために、詳細用の API に operation パラメーターを追加しています。また、戻り型として JSON を指定しており、こうすることで JavaScript から返されるデータの解析が容易になります。これでアプリケーション・コードによる呼び出しの内容を理解できたので、このアプリケーションがどのように呼び出しを行い、どのように Foursquare のデータを利用するのかを調べてみましょう。

ジオロケーションを使う

最初の呼び出しは検索です。リスト 5 から、緯度と経度用に geoLatgeoLong という 2 つのパラメーターが必要なことがわかります。下記のリスト 6 は、この 2 つのパラメーターをアプリケーションの中で取得する方法、そしてサーブレットを呼び出す方法を示しています。

リスト 6. 位置に関する検索を呼び出す
if (!!navigator.geolocation){
    navigator.geolocation.getCurrentPosition(function(location) {
        venueSearch(location.coords.latitude, location.coords.longitude);
    });
}
var allVenues = [];
function venueSearch(geoLat, geoLong){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200){
            var responseObj = eval('(' + this.responseText + ')');
            var venues = responseObj.groups[0].venues;
            allVenues = venues;
            buildVenuesTable(venues);
        }
    }
    xhr.open("GET", "api?geoLat=" + geoLat + "&geoLong="+geoLong);
    xhr.send(null);
}

上記のコードはブラウザーにジオロケーション機能があるかどうかを調べます。ジオロケーション機能がある場合には、このコードは位置情報を取得し、取得した緯度と経度を引数として venueSearch 関数を呼び出します。この関数では Ajax (リスト 5 のサーブレットを呼び出す XMLHttpRequest オブジェクト) が使用され、venueSearch 関数は Foursquare から返される JSON データをコールバック関数のクロージャーを使用して解析し、venue オブジェクトの配列を buildVenuesTable という関数に渡します (リスト 7)。

リスト 7. venue から UI を作成する
function buildVenuesTable(venues){
    var rows = venues.map(function (venue) {
        var row = document.createElement("tr");
        var nameTd = document.createElement("td");
        nameTd.appendChild(document.createTextNode(venue.name));
        row.appendChild(nameTd);
        var addrTd = document.createElement("td");
        var addrStr = venue.address + " " + venue.city + "," + venue.state;
        addrTd.appendChild(document.createTextNode(addrStr));
        row.appendChild(addrTd);
        var distTd = document.createElement("td");
        distTd.appendChild(document.createTextNode("" + venue.distance));
        row.appendChild(distTd);
        return row;
    });
    var vTable = document.createElement("table");
    vTable.border = 1;
    var header = document.createElement("thead");
    var nameLabel = document.createElement("td");
    nameLabel.appendChild(document.createTextNode("Venue Name"));
    header.appendChild(nameLabel);
    var addrLabel = document.createElement("td");
    addrLabel.appendChild(document.createTextNode("Address"));
    header.appendChild(addrLabel);
    var distLabel = document.createElement("td");
    distLabel.appendChild(document.createTextNode("Distance (m)"));
    header.appendChild(distLabel);
    vTable.appendChild(header);
    var body = document.createElement("tbody");
    rows.forEach(function(row) {
        body.appendChild(row);
    });
    vTable.appendChild(body);
    $("searchResults").appendChild(vTable);
    if (!!window.openDatabase){
        $("saveBtn").style.display = "block";
    }
}

リスト 7 のコードは基本的に、venue 情報を中に含むデータ・テーブルを作成するための DOM コードです。ただし、いくつか興味深い点があります。配列オブジェクトのマップや forEach 関数など、高度な JavaScript 機能が使われていることに注目してください。これらの機能は、ジオロケーションをサポートするすべてのブラウザーで利用することができます。もう 1 つ興味深い点は最後の 2 行です。この 2 行ではデータベース・サポート機能を検出しています。データベース・サポート機能がある場合には、Save ボタンを有効にします。ユーザーが Save ボタンをクリックすると、venue データがすべてローカル・データベースに保存されます。次のセクションでは、この動作について説明します。

構造化されたストレージ

リスト 7 は昔ながらの Progressive Enhancement 手法を示しています。この例ではデータベース・サポートがあるかどうかを調べ、データベース・サポートがある場合、このデータベース・サポートを利用した新機能をアプリケーションにもたらす UI 要素が追加されます。この例の場合には 1 つのボタンが有効になります。このボタンをクリックすると、saveAll という関数が呼び出されます (リスト 8)。

リスト 8. データベースに保存する
var db = {};
function saveAll(){
    db = window.openDatabase("venueDb", "1.0", "Venue Database",1000000);
    db.transaction(function(txn){
        txn.executeSql("CREATE TABLE venue (id INTEGER NOT NULL PRIMARY KEY, "+
                "name NVARCHAR(200) NOT NULL, address NVARCHAR(100), 
cross_street NVARCHAR(100), "+
                "city NVARCHAR(100), state NVARCHAR(20), geolat TEXT NOT NULL, "+
                "geolong TEXT NOT NULL);");
    });
    allVenues.forEach(saveVenue);
    countVenues();
}
function saveVenue(venue){
    // check if we already have the venue
    db.transaction(function(txn){
        txn.executeSql("SELECT name FROM venue WHERE id = ?", [venue.id],
            function(t, results){
                if (results.rows.length == 1 && results.rows.item(0)['name']){
                    console.log("Already have venue id=" + venue.id);
                } else {
                    insertVenue(venue);
                }
            })
    });
}
function insertVenue(venue){
    db.transaction(function(txn){
        txn.executeSql("INSERT INTO venue (id, name, address, cross_street, "+
                "city, state, geolat, geolong) VALUES (?, ?, ?, ?, "+
                "?, ?, ?, ?);", [venue.id, venue.name, 
                 venue.address, venue.crossstreet, venue.city, venue.state,
                 venue.geolat, venue.geolong], null, errHandler);
    });        
}
function countVenues(){
    db.transaction(function(txn){
        txn.executeSql("SELECT COUNT(*) FROM venue;",[], function(transaction, results){
            var numRows = results.rows.length;
            var row = results.rows.item(0);
            var cnt = row["COUNT(*)"];
            alert(cnt + " venues saved locally");
        }, errHandler);
    });
}

venue データをデータベースに保存するためには、まずデータの保存用のテーブルを作成します。テーブルの作成には非常に標準的な SQL 構文を使います。(データベースをサポートするブラウザーはすべて SQLite を使います。サポートされるデータ型、制約、等々については SQLite のドキュメントを参照してください。) SQL の実行は非同期で行われます。トランザクション関数が呼び出され、このトランザクション関数にコールバック関数が渡されます。コールバック関数は SQL を実行するためのトランザクション・オブジェクトを取得します。executeSQL 関数は SQL ストリングを引数に取り、さらにオプションとしてパラメーター・リスト、そして SQL 実行時の成功およびエラーを処理するためのハンドラー関数を引数に取ります。エラー・ハンドラーがない場合には、そのエラーは消えてしまいます。これは create table 文の場合には望ましい動作です。このスクリプトを初めて実行すると、テーブルが適切に作成されます。このスクリプトをもう一度実行すると、テーブルは既に存在するためスクリプトは失敗しますが、これは問題ありません。テーブルに行の挿入をする前に、確実にテーブルが存在するようになってさえいればよいからです。

テーブルを作成したら、forEach 関数と Foursquare から返された各 venue を使って saveVenue 関数を呼び出します。saveVenue 関数は最初に、その venue が既にローカルに保存されているかどうかを確認するために、その venue に対するクエリーを実行します。ここで成功用のハンドラーが使われていることがわかると思います。このクエリーによる結果セットが成功用のハンドラーに渡されます。結果がない場合、つまりその venue がまだローカルに保存されていない場合には、insertVenue 関数が呼び出され、この関数が insert 文を実行します。

saveAll の場合、すべての保存、挿入を完了した後、countVenues 関数を呼び出します。この関数は、venue テーブルに挿入された合計行数がわかるように問い合わせをします。ここで使われている構文 (row["COUNT(*)"]) はクエリーの結果セットから合計行数を抽出しています。

これで、データベース・サポート機能が存在する場合にこの機能を利用する方法を理解できたので、次のセクションでは Web ワーカーのサポートを利用する方法について調べます。

Web ワーカーによるバックグラウンド処理

リスト 6 に戻り、少し変更を加えましょう。下記のリスト 9 に示すように、Web ワーカーがサポートされているかどうかをチェックします。サポートされている場合には、Foursquare から返された各 venue の詳細情報を、Web ワーカーを使用して取得します。

リスト 9. venue 検索の変更版
function venueSearch(geoLat, geoLong){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200){
            var responseObj = eval('(' + this.responseText + ')');
            var venues = responseObj.groups[0].venues;
            allVenues = venues;
            buildVenuesTable(venues);
            if (!!window.Worker){
                var worker = new Worker("details.js");
                worker.onmessage = function(message){
                    var tips = message.data;
                    displayTips(tips);
                };
                worker.postMessage(allVenues);
            }
        }
    }
    xhr.open("GET", "api?geoLat=" + geoLat + "&geoLong="+geoLong);
    xhr.send(null);
}

上記のコードは先ほどと同じ検出方法を使用しています。Web ワーカーがサポートされている場合には、新しいワーカーを作成します。新しいワーカーを作成するためには、そのワーカーによって実行される別のスクリプト (この場合は details.js ファイル) を指す URL が必要です。ワーカーは作業を終了すると、メイン・スレッドにメッセージを返します。このメッセージを受信するのは onmessage ハンドラーであり、このハンドラーには単純なクロージャーを使用します。最後に、ワーカーを起動するために、そのワーカーの作業対象となるデータを持つ postMessage を呼び出します。そして Foursquare から取得したすべての venue を渡します。リスト 10 には、ワーカーによって実行されるスクリプト details.js の内容を示してあります。

リスト 10. ワーカーのスクリプト details.js
var tips = [];
onmessage = function(message){
    var venues = message.data;
    venues.foreach(function(venue){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            if (this.readyState == 4 && this.status == 200){
                var venueDetails = eval('(' + this.responseText + ')');
                venueDetails.tips.forEach(function(tip){
                    tip.venueId = venue.id;
                    tips.push(tip);
                });
            }
        };
        xhr.open("GET", "api?operation=getDetails&venueId=" + venueId, true);
        xhr.send(null);
    });
    postMessage(tips);
}

この details スクリプトは各 venue に対して繰り返し処理を行います。このスクリプトは venue ごとに Foursquare プロキシーへのコールバックを行い、おなじみの XMLHttpRequest を使ってその venue の詳細を取得します。ただし、XMLHttpRequest の open 関数を使って接続を開く場合に 3 番目のパラメーター (true) を渡していることに注意してください。こうすることで、呼び出しが通常の非同期ではなく同期型になります。ワーカーはメインの UI スレッドで実行されているわけではないので、ワーカーから同期型の呼び出しを行っても問題なく、これによってアプリケーションがフリーズすることはありません。呼び出しを同期型にすることで、次の呼び出しを開始する前に各呼び出しを終了する必要があることがわかります。このハンドラーは単純に venue の詳細から tip (口コミ) を抽出し、これらの tip をすべて収集してメインの UI スレッドに戻り値として渡します。このデータを渡す動作のために postMessage 関数が呼び出され、この関数がワーカーの onmessage コールバック関数を呼び出します (リスト 9)。

デフォルトで、venue を検索すると 10 件の venue が返されます。詳細の取得にはさらに 10 回の呼び出しを行う必要があり、それにどれほど時間がかかるか想像してみてください。こうした種類の作業はバックグラウンド・スレッドで Web ワーカーを使って行うことが適切なのです。


まとめ

この記事では、最新のブラウザーに導入された、HTML 5 の新機能のいくつかを説明しました。そして、これらの機能を検出する方法を学び、新機能が存在する場合にはそれらをアプリケーションに漸進的に追加する方法を学びました。これらの機能の大部分は、一般的なブラウザー、特にモバイル・ブラウザーでは既に広くサポートされています。これで皆さんも、ジオロケーションや Web ワーカーなどの機能を利用して革新的な Web アプリケーションの作成を開始することができます。


ダウンロード

内容ファイル名サイズ
Article source codeFutureWeb.zip9KB

参考文献

学ぶために

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

議論するために

コメント

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=485851
ArticleTitle=HTML 5 を利用した Web アプリケーションを作成する
publish-date=03302010