japan.internet.com
japan.internet.com メンバーID
Twitter
Facebook
RSS
ピックアップ
2007年3月20日 14:00

PrototypeでAJAX開発を効率化

はじめに

 Prototypeのホームページには、「Prototypeは動的なWebアプリケーションの開発を簡易化することを目的としたJavaScriptフレームワークである」と書かれています。Prototypeは、クラス主導の開発や継承など多くのオブジェクト指向の手法を使ってJavaScriptアプリケーションを開発できるようにすることで、その目的を完全に果たしています。事実、このフレームワークには役に立つ機能が数多く用意されており、一度使い始めたら、どのアプリケーションの開発にも利用したくなります。

 Prototypeの最大の特徴は、その機能豊富なAJAX(Asynchronous JavaScript and XML)ライブラリにあり、これによって、JavaScriptを介してサーバの非ブロック呼び出しを行うという人気のWeb開発手法を簡単に取り入れることができます(昨年度のAjaxian.com 2006 Surveyでは、Prototypeは最も人気の高いAJAXフレームワークという評価を受けました)。もちろん、Prototypeで実現できることはすべて、Prototypeのベースである普通のJavaScriptでも実現できます。しかし、わざわざ仕事を増やす必要はありません。例えば、同じタスクを行うのにdocument.getElementById("myElement")$("myElement")のどちらを使いたいですか? 本稿では、$("myElement")を選んだ読者のために、$をはじめとする役に立つPrototype関数を詳しく説明します。

 とはいえ、Prototypeは巨大なフレームワークなので、1回では説明しきれません。そこで、ここではごく一部の重要な機能だけを紹介します。フレームワーク全体を深く理解するには、ソースコードを見てください。基本な仕組みを知るにはソースコードを見るのが一番であり、必要に合わせて修正を加えることもできます。

便利な関数

 WebアプリケーションでPrototypeを使うには、まず最新のバージョンをダウンロードします(本稿執筆時の最新バージョンは1.4.0)。実際に必要なファイルは「prototype.js」だけで、次のように<script>タグを使ってページにインクルードします。

<script type="text/javascript" src="yourPath/prototype.js"></script>

 先ほど取り上げた$関数は、document.getElementByIdのショートカットです。$には複数の要素IDを渡すこともできます。次のコードを実行すると、要求された要素を含む配列が返されます。

<!-- HTML -->
<div id="firstElem">First element’s content</div>
<div id="secondElem">Second element’s content</div>

// JavaScript
var elems = $("firstElem", "secondElem");
alert(elems[1].innerHTML);  //displays "Second element’s content"

 もう1つの実に役に立つショートカット関数は、フォームフィールドIDを取得してフィールド値を返す$Fです。

<!-- HTML -->
<select name="language" id="language">
 <option value="JS" selected="selected">JavaScript</option>
 <option value="Java">Java</option>
 <option value="C#">C#</option>
 <option value="Ruby">Ruby</option>
</select>

// JavaScript
var langValue = $F("language");
alert(langValue); //displays "JS"

オブジェクト指向(Object-Oriented:OO)のJavaScript

 Prototypeでは、OOスタイルに非常によく似たJavaScriptコードを作成することもできます。例えば、次のクラス定義を見てみましょう。

// Class definition
var Person = Class.create();

// Class methods
Person.prototype =
{
    //Constructor
    initialize : function(firstName, lastName, age)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    },

    //method #1
    toString : function()
    {
        return this.firstName + " " + this.lastName + ", " + this.age;
    },

    //method #2
    displayFirstName : function()
    {
        alert(this.firstName);
    }
};

var myPerson = new Person("Alessandro", "Lacava", 30);
alert(myPerson.toString()); //displays "Alessandro Lacava, 30"

 見ての通り、コンストラクタは(慣例により)initializeメソッドを使って定義されています。さらに、この例では他にtoStringdisplayFirstNameの2つのメソッドも定義されています。もちろん、メソッドは必要な数だけ定義できます。

 また、Prototypeでは、Object.extendメソッドによって既存のクラスを拡張することもできます。このメソッドは、あるオブジェクトのプロパティとメソッドを別のオブジェクトにコピーします。構文はObject.extend(destination, source)です。ここでdestinationにはsourceのプロパティとメソッドで拡張するオブジェクトを指定します。

「this」の参照先が期待どおりではない場合

 既にご存知のように、JavaScriptの関数は本格的なオブジェクトです。PrototypeはJavaScriptのFunctionクラスにさらに2つの素晴らしいメソッドbindbindAsEventListenerを追加します。この2つのメソッドは、イベントハンドラの働きを設定するときに非常に役に立ちます。これらのメソッドの利点をよく理解するために、次のコードを見てみましょう。このコードでは、Personオブジェクトを作成し、そのdisplayFirstNameメソッドを<div>のクリックイベントのイベントリスナとして設定します。

<!-- HTML -->
<div id="elem">Click Here</div>

// JavaScript
var person = new Person("John", "Brown", 20);
$("elem").onclick = person.displayFirstName;

 ここでの目的は、ユーザーが「Click Here」をクリックしたときにファーストネーム(John)を表示することです。しかし、クリックしてみても、undefinedが表示されてしまいます。なぜかと言うと、$("elem").onclick = person.displayFirstNameというコードは、「<div>onclick関数の本体をperson.displayFirstName関数の本体で置き換える」、つまりalert(this.firstName);に置き換えることを事実上意味しているからです。

 問題は、onclickが呼び出されたときに、thisキーワードがもはやPersonを参照していないことです。thisが参照するのは、firstNameプロパティを持っていない<div>要素です。Prototypeでは、bindメソッドを次のように利用してこの問題を見事に解決できます。

$("elem").onclick = person.displayFirstName.bind(person);

 これで、thisキーワードはPersonクラスを参照するようになります。このコード行を言葉で表現するなら、「<div>onclickの本体をperson.displayFirstNameの本体で置き換え、かつ、thisキーワードとpersonオブジェクトをbindでバインドする」となります。bindAsEventListener関数は同じことを実行するだけでなく、クロスブラウザ方式で呼び出された関数にeventオブジェクトを渡します。

Prototypeによる反復

 Prototypeでは、配列の反復処理に役に立つメソッドを備えたEnumerableオブジェクトが定義されています。配列の反復処理が可能になったのは、Arrayが(Object.extendを介して)Enumerableで拡張されたためです。従来のJavaScriptでは、配列の反復処理を行うには次のようなコードを記述していました。

var languages = [’JavaScript’, ’Java’, ’C#’, ’PHP’, ’C++’, ’Ruby’];

for(var i = 0; i < languages.length; i++)
{
    alert(languages[i]);
}

 これをEnumerableeachメソッドを使って書き直すと、次のようになります。

var languages = [’JavaScript’, ’Java’, ’C#’];

languages.each(
    function(value, index)
    {
        alert(value);
    }
);

 eachメソッドは、他の多くのEnumerableメソッドと同様に、関数を引数として受け取ります。この場合の関数をイテレータと呼びます。1つ目のパラメータ(value)は現在の要素の値であるのに対し、indexは反復の現在のインデックスです。indexパラメータは不要であれば省略することもできます。例えば、次のコードは先ほどのコードと同じ結果になります。

languages.each(
    function(value)
    {
        alert(value);
    }
);

 each自体はここまでに取り上げた機能ほど役に立つものではありませんが、Enumerableには、Prototypeをフレームワークとして選びたいと思わせる有用なメソッドがたくさん含まれています。例えば、grepメソッドでは、正規表現パターンに一致する配列のサブセットを選択することができます。

// it displays Java and JavaScript
languages.grep(/Java.*/, function(value)
    {
        alert(value);
    }

);

 このコードは、「Javaという単語から始まるすべての要素を選択し、見つかった各項目を表示する」という意味です。

 複雑なフィルタ処理が必要なければ、次のように単にselectメソッドを使うことができます。

var prices = [10, 12, 25, 28, 50, 100];

// lowPrices = [10, 12, 25]
var lowPrices = prices.select( function(value)
        {
    return value <= 25;
    }
);

 この例では、イテレータ関数がtrueを返す要素を選択します。

有益なメソッド

 Prototypeでは、Eventオブジェクトを使用してクロスブラウザの手法でイベントを処理することができます。例えば、observeメソッドと併用することで要素を監視できます。observeメソッドが受け取るパラメータは次の4つです。

  • 監視する要素
  • イベントのタイプ(click、blur、keyupなど)
  • イベントが発生したときに呼び出す関数
  • キャプチャフェーズを使う必要があるかどうか

 次に例を示します。

<!-- HTML -->
<div id="elem">
  Click Here
</div>

//Javascript
Event.observe($("elem"), "click", displayAlert, false);

function displayAlert()
{
    alert("You clicked the element");
}

 要素の監視を中止するには、次のようにstopObservingメソッドを呼び出します。

Event.stopObserving($("elem"), "click", displayAlert, false);

 もう1つの役に立つオブジェクトはElementです。例えば、このオブジェクトのtoggleメソッドでは、要素が表示されている場合は非表示に、非表示の場合は表示に、要素の状態を切り替える(トグルする)ことができます。

<!-- HTML -->
<input type="button" value="Click here" 
 onclick="javascript:Element.toggle(’toggable’)">

<div id="toggable">
    You can toggle me by clicking the button above.
</div>

 さらに、要素を表示するためのshowと、要素を非表示にするためのhideという2つの効果的なメソッドもあります。

 これで、AJAXによるプログラミングの準備が整いました。Prototypeには、Web 2.0のアプリケーションやサイトの開発に便利なツールが数多く用意されています。

Prototypeを利用したAJAX開発

 AJAXの柱は、非同期の要求をサーバに送信するXMLHttpRequest(XHR)オブジェクトです。問題は、このオブジェクトはブラウザ固有なので、インスタンス作成時にブラウザを考慮しなければならないことです。Prototypeを使えば、この問題をはじめ、ブラウザ固有の実装を心配する必要がなくなります。さらに、普通のJavaScriptでは多くの処理を必要とする複雑なタスクも、フレームワークでは数行のコードで実行することができます。例えば、次に示すAjax.Updaterメソッドは、サーバから「page.htm」ファイルの内容を取り出し、返された値をmyElem <div>に挿入します。

<!-- HTML -->
<div id="myElem">
</div>

// JavaScript
new Ajax.Updater(
    "myElem",
    "page.htm",
    {
        onFailure : function() {
            alert("An error occurred");
            }
    }
);

 このようにわずか数行で「page.htm」の中味を取得し、それをmyElem <div>に挿入することができます。また、PHPファイルなどのサーバコンポーネントを呼び出し、それらを必要な数だけパラメータとして引き渡すこともできます。

new Ajax.Updater(
    "myElem",
    "component.php",
    {
        parameters : "param1=value1&param2=value2",
        method : "post",
         onFailure : function() {
            alert("An error occurred");
            }
    }
);

 この場合、パラメータはクエリ文字列として渡されます。また、methodの使い方にも注目してください。ここには使用するHTTPメソッドを指定しますが、仮に他のHTTPメソッドがある場合でも、基本的にはGETまたはPOSTを指定します。一般に、Ajax.Updaterが取得するパラメータは次の3つです。

  1. 挿入する要素のID
  2. 呼び出すサーバコンポーネント
  3. オプションオブジェクト(さまざまなオプションを使用できるが、上記のコード例にあるオブジェクトが最も一般的)

 もう1つの役に立つクラスはAjax.PeriodicalUpdaterです。このクラスを使ってサーバを定期的にポーリングすることができます。例えば、これを使ってニュースサーバを時々クエリし、ニュースが更新されたときにページを更新することができます。次に例を示します。

new Ajax.PeriodicalUpdater(
    "myElem",
    "newsProvider.php",
    {
        frequency : 5,
        decay : 2
    }
);

 1つ目と2つ目のパラメータの意味はAjax.Updaterの場合と同じです。frequencyパラメータは、Ajax.PeriodicalUpdaterがサーバをポーリングした後の秒数です。decayパラメータは、応答が変わらない場合にサーバのポーリング間隔を長くするために使われます。この例では、5秒後にサーバが前回の要求と同じ応答を返した場合、新しいポーリング間隔は10秒になります(つまり、frequencydecayを掛けます)。10秒後も応答が同じであれば、間隔は20秒になります。異なる応答が返ってきた場合は、直ちにポーリング間隔は5秒にリセットされ、振り出しに戻ります。

 サーバが、HTMLコードではなくXMLやJSONなどの他のデータ形式を返す場合は、別の強力なクラスAjax.Requestを使うことができます。このクラスを使用すると、サーバに要求を送ってXMLHttpRequestオブジェクトを受け取り、このオブジェクトからあらゆる種類のサーバ応答を取得することができます。HTML以外の、最も一般的な応答タイプはXMLとJSONです。Ajax.Requestの簡単な使用例を次に示します。

new Ajax.Request(
    "component.php",
    {
         onSuccess  : processResponse,
         onFailure  : notifyFailure,
         parameters : "param1=value1&param2=value2"
    }
);

function processResponse(xhrObject)
    {
        alert("The response is: " + xhrObject.responseText);
    }

function notifyFailure()
    {
        alert("An error occurred!");
    }

 このコードでは、いくつかのパラメータを使ってサーバへの非同期要求を行います。応答が成功した場合は、processResponse関数を呼び出してXMLHttpRequestオブジェクトを受け取ります。この関数は、サーバから取得したものを何でも表示します。応答が失敗した場合は、notifyFailure関数を呼び出します。

 もう1つよく目にするのが、AJAXアクティビティがあると必ず通知(またはイメージ)を表示するタスクです。これは、要求の実行に時間がかかる場合に非常に役に立ちます。まさにこれを実現するのがAjax.Respondersregisterメソッドです。簡単な使用例を次に示します。

<!-- HTML -->
<div id="loading" style="visibility: hidden;">
    <img src="loading.gif" />
</div>

// JavaScript
Ajax.Responders.register(
    {
        onCreate : displayLoading,
        onComplete : hideLoading
    }
);

function displayLoading()
    {
        Element.show("loading");
    }

function hideLoading ()
    {
        Element.hide("loading");
    }

 この例では、AJAX呼び出しが始まると直ちにGIFが表示されます。応答を受け取ると、ロード中のGIFは非表示になります。Element.showメソッドとElement.hideメソッドの使い方にも注目しましょう。

AJAX開発に役立つ機能

 基本的に、AJAXではサーバに要求を送って応答を受け取るという処理を行います。この場合、要求と一緒に、クエリ文字列の形式(つまり、param1=value1&param2=value……)でパラメータを送ることが必要になることも少なくありません。Prototypeには、クエリ文字列を非常に簡単に作成できる便利な機能が用意されています。例えば、1つのフォームがあり、そのフォームのすべてのフィールドをクエリ文字列として集めたい場合は、Form.serializeメソッドを使うことができます。次に例を示します。

<!-- HTML -->
<form id="myForm" name="myForm">
    First Name:
    <input type="text" name="firstName" id="firstName" />
    <br />
    Last Name:
    <input type="text" name="lastName" id="lastName" />
    <br />
    Gender:
    <br />
    <input type="radio" name="gender" id="gender" 
        value="M" checked="true"/>
    Male
    <br />
    <input type="radio" name="gender" id="gender" value="F" />
    Female
    <br />
    <select id="state" name="state" multiple>
        <option value="ALA">Alabama</option>
        <option value="CAL">California</option>
        <option value="FLO">Florida</option>
    </select>
    <br />
    <input type="button" name="clickButton" 
        id="clickButton" value="Click Me"
        onclick="displayQueryString()" />
</form>

// JavaScript
function displayQueryString()
{
    var qs = Form.serialize($("myForm"));
    alert(qs);
}

 このフォームに私の個人的な情報を入力し、stateフィールドに「California」を指定してみたところ、図1の画面が表示されました。

図1 Form.serialize関数
図1 Form.serialize関数

 簡単かつ簡潔です。また、複数選択も可能であり、複数の値を選択した場合でも、Prototypeが正しいクエリ文字列の組み合わせを自動的に処理してしてくれます。例えば、前述のフォームで「California」と「Florida」を選択した場合は、次のクエリ文字列が返されます。

firstName=Alessandro&lastName=Lacava&gender=M&’’state=CAL&state=FLO’’

 Form.serializeを利用する場合は、次の2つのことを覚えておきましょう。

  1. イメージとボタン入力は無視される。
  2. submitボタンはクエリ文字列に含まれる。例えば、前述の例でtype="button"の代わりにtype="submit"を使った場合は、クエリ文字列中にclickButton=Click Meが含まれる。

 では、フォームではなくオブジェクトをシリアライズしたい場合はどうしたらよいでしょうか。当然、Prototypeはこのことも考えています。次のコードを見てみましょう。

// create the object
var obj = {
              firstName : "Alessandro",
              lastName  : "Lacava",
              gender    : "M",
              state     : "CAL"
           };
// get a query string from it
var qs = $H(obj).toQueryString();
alert(qs);

 このコードの結果は図1と同じです。このコードで重要な役割を果たすのが$H関数です。この関数はobjectパラメータをハッシュに変換します(つまり、オブジェクトのプロパティ名をキー、オブジェクトのプロパティ値を値とする、キーと値のペアの集合が作られます)。

 ここまでPrototypeの数々の優秀な機能を紹介してきましたが、必要に合わせてソースコードを修正できることを忘れてはなりません。例えば、前述したとおり、Form.serializeはデフォルトでクエリ文字列にsubmitボタンを含めます。この振る舞いを変更したい場合は、ソースコードを変更します。「prototype.js」ファイルを開くと、次のコードブロックが表示されます。

Form.Element.Serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case ’submit’:
      case ’hidden’:
      case ’password’:
      case ’text’:
        return Form.Element.Serializers.textarea(element);
      case ’checkbox’:
      case ’radio’:
        return Form.Element.Serializers.inputSelector(element);
    }
    return false;
  },
 :
 :
 :

 case ’submit’:の行をコメントアウト(または削除し)、ファイルを保存すると、Form.serializeを使ったときに入力タイプのsubmitが認識されなくなります。

上位ライブラリの基盤としてのPrototype

 Prototypeが優れたフレームワークであるということは、Prototypeをベースとして開発された上位ライブラリがあることからも分かります。このような上位ライブラリで最も有名なのが、ScriptaculousRicoです。どちらもUI開発に使用され、AJAXを完全にサポートしています。

 これらのライブラリの特徴を知るため、Scriptaculousのダウンロードページから最新のバージョンをダウンロードしましょう(本稿執筆時の最新バージョンは1.6.5で、アーカイブに含まれているPrototypeの使用バージョンは1.5.0_rc1)。必要なファイルは「prototype.js」「scriptaculous.js」「builder.js」「effects.js」「dragdrop.js」「slider.js」「controls.js」です。これらのファイルをディレクトリ(「scripts」など)に入れます。

 Scriptaculousを使うには、最初の2ファイルだけをインクルードします。

<script src="./scripts/prototype.js" type="text/javascript"></script>
<script src="./scripts/scriptaculous.js" type="text/javascript">
</script>

 その他のファイルは「scriptaculous.js」によって自動的にインポートされます。Scriptaculousは、アニメーション効果、ドラッグ&ドロップ、AJAXのコントロール、DOMのユーティリティなどを含む、リッチなUIライブラリです。次に、アニメーション効果の例を示します。

<!-- Pulsate Effect -->
<div id="pulsateTest" style="border: 1px solid #0000ff; width: 100px;"
        onclick="new Effect.Pulsate($(’elemToShow’))">
    Click to see the pulsate effect
</div>

<br /><br />

<!-- Puff Effect -->
<div id="puffTest" style="border: 1px solid #0000ff; width: 100px;"
        onclick="new Effect.Puff($(’elemToShow’))">
    Click to see the puff effect
</div>

<br /><br />

<div id="elemToShow" style="width: 200px; border: 1px solid red;">
    This element was created to show some of the Scriptaculous
    animation effects.
</div>

 <div>要素をクリックしたとたん、elemToShow <div>の素敵なアニメーション効果を体験することができます。アニメーション効果のために必要なコード行は1行だけで、一般的な構文はnew Effect.EffectName(element);です(elementは効果を適用する要素、EffectNameは使用する効果の名前です)。Scriptaculousには、Appear、Fade、Puff、BlindDown、BlindUp、SwitchOffなど、数多くの効果が用意されています。また、さまざまなオプションを使って効果をカスタマイズすることもできます。すべてのアニメーション効果およびこのライブラリのその他の機能については、Scriptaculousのデモページをご覧ください。

 Ricoも、AJAX開発に役立つUIライブラリです。本稿執筆時の最新バージョンは、Prototype 1.4.0をベースとする1.1.2でした。このライブラリはRicoのWebサイトからダウンロードできます。Scriptaculousと同様、Ricoを使うにはWebページに次の2つのインクルードが必要になります。インクルードするファイルが「scripts」フォルダに入っている場合は、次のように指定します。

<script type="text/javascript" src="./scripts/prototype.js"></script>
<script type="text/javascript" src="./scripts/rico.js"></script>

 AJAXやドラッグ&ドロップなどの素敵な機能のほか、Ricoには非常におもしろいシネマ効果も用意されています。例えば、丸みのあるボックスの中にコンテンツを表示したいと思ったことはありませんか? Ricoならこれを簡単に実現できます。次に例を示します。

<!-- Round Effect -->
<div id="roundTest" style="border: 1px solid #0000ff; width: 100px;"
        onclick="Rico.Corner.round($(’elemToRound’))">
    Click to round the div
</div>

<br /><br />

<div id="elemToRound" 
    style="background-color: #0000ff; color: #ffffff; width: 200px;">
    This element was created to round its corners using the Rico.Co
rner.round method.
</div>

 「Click to round the div」をクリックすると、図2が表示されます。

図2 丸みのあるボックス
図2 丸みのあるボックス

 もちろん、Ricoにも、すべての効果をカスタマイズするためのオプションが数多く用意されています。このライブラリの詳細については、Ricoのデモページをご覧ください。

Prototypeのパワー

 Prototypeフレームワークがいかに強力か、ご理解いただけたことと思います。非常に優れているからScriptaculousやRicoなどの素晴らしいUIライブラリの基盤として利用されているのです。Prototypeの機能をすべて身に付ければ、後は皆さんのイマジネーションだけで、次世代のWeb 2.0アプリケーションを簡単に実現することができます。

著者紹介

Alessandro Lacava(Alessandro Lacava)
イタリアを活動拠点とするソフトウェア開発者、テクニカルライター。Javaと.NETテクノロジ、Webアプリケーション、通信システムを得意分野とする。通信エンジニアリングの資格を保有。連絡先については個人のWebサイトを参照。

関連テーマ
プリンター用
記事を転送
この記事をクリップ!
厳選した九州のお野菜とお米をお届け
厳選した九州のお野菜とお米をお届け 野菜の木では、老舗料亭 沙羅の木が厳選した九州のお野菜とお米をお届けします。 毎週、隔週での定期のご購入も可能です。 入会費、年会費、送料、荷造手数料は無料です。
注目のトピックス
Copyright 2012 internet.com K.K. (Japan) All Rights Reserved.