prototype.jsのAjax.Requestメソッドを使う場合、パラメータの受け渡し時にアンパサンド(&)などの特別な文字をエスケープする必要があります。そのときに、encodeURIComponent()メソッドで、パラメータをURIエンコードしてしまうのが楽です。
var message = "foo&bar";
var url = "/foo/bar.pl"
var pars = "message=" + encodeURIComponent(message);
var Ajax = new Ajax.Request(
url,
{
method: 'get',
parameters: pars,
onCreate: show_spinner,
onComplete: show_response
});
messageの値は、foo%26barに変換されます。
DHTMLというと、なんだか死語のような気もします。マウスストーカーとか揺れるウィンドウとか、目障りな視覚効果のせいで悪い印象が付いてしまったのが原因かもしれません。その野暮ったさを払拭するためなのか、最近はこういうのを全部ひっくるめてAjaxと呼ぶようです。非同期でもXMLでもないAjaxとは、木製鉄棒、みたいな感じでしょうか。
Webにあるドラッグ&ドロップ関連のJavaScriptは古いものが多かったので、新しいものを書いてみました。JavaScriptとDOMを使っています。WindowsではIE7、Firefox2、Opera9、MacではSafari、Firefox2、Opera9で動作チェック済み。
Ajaxの郵便番号検索ガジェットです。郵便局が発行するデータベースを使っています。
郵便番号検索はシンプルなプログラムなので、Ajaxによるデータの連携方法がよくわかります。処理の流れは次のようになっています。
Ajaxとは直接は関係ありませんが、読み込み中のエフェクトは、せっかくだし付けておきたいものです。これはprototype.jsのAjax.RequestとアニメーションGIFを使って簡単に実現できますが、ちょっとした落とし穴があり解決に相当手間取ったので、メモを残しておきます。
var Ajax = new Ajax.Request(
url,
{
method: 'get',
parameters: par,
// onLoading: show_spinner, // NG
onCreate: show_spinner, // OK
onComplete: show_response
});
show_spinnerは回転するGIFアニメーションを表示する関数で、show_responseは取得したデータを表示する関数です。自然な発想として、onLoadingでshow_spinner呼び出して、onCompleteでshow_responseを呼び出したいところですが、これではうまくいかない場合があります。
というのも、onComplete後にonLoadingが呼び出されてしまうことがあるためです。Firefoxでは大丈夫でしたが、IE 7で頻発しました。そこで、onCreateでGIFアニメーションを表示すると、すっきり解決します。
基本的にJavaScriptでは音声を扱えません。しかし、ちょっとした音声効果を追加するのにFlashを導入するのも面倒です。そういうときは、Gustavo Ribeiro AmigoのJavascript Sound KitというFlashラッパーで解決できます。これは、JavaScriptからActionScriptのサウンドオブジェクトを呼び出して、MP3ファイルを再生可能にしてくれるクラスです。
まず、soundkt-0.1.zipをダウンロードします。展開するといくつかのサンプルファイルが入っていますが、使うのはSound.js(Soundクラスのファイル)とSoundBridge.swf(MP3を再生するための音声ブリッジ)です。
Sound.jsを開いて、swfLocationプロパティにSoundBridge.swfへのパスを記述します。
this.options.swfLocation = "/path/to/SoundBridge.swf"
使用例はこんな感じです。メソッドを直接呼び出すとなぜかエラーが出てしまうので、まずはインスタンスだけを作り、メソッドはほかの関数から呼び出しています(仕様なのかバグなのか、よくわかりません)。
音声を操作するプロパティもあるので、ちょっとした音声プレーヤーをJavaScriptで書くこともできます。
<html>
<head>
<script src="/js/Sound.js" type="text/javascript"></script>
<title>Javascript Sound Kit</title>
</head>
<body>
<script type="text/javascript">
var mysound = new Sound();
function get_sound(arg) {
mysound.loadSound("/sound/alarm.mp3", true);
if (arg == "start") { mysound.start(); }
if (arg == "stop") { mysound.stop(); }
}
</script>
<input type="button" value="Start" onclick="get_sound('start');" />
<input type="button" value="Stop" onclick="get_sound('stop');" />
</body>
</html>
12桁表示の電卓です。プロトタイプベースで仕上げてあります。『JavaScript 第3版』と『DOM Scripting 標準ガイドブック』を読みながらコードを書きましたが、どちらも良書だと思いました。この2冊を読めば、必ずJavaScriptは脱コピペ宣言できます。DOM Scriptingに気を使うと、ソースコードも頭もすっきりして、気持ちがよいです。
今まで私は、画面更新にinnerHTMLを多用していましたが、DOM Scriptingで推奨されているcreateTextNodeを使ってみると溜飲が下がりました。ノードの考え方が身に付ければ、応用の幅が広がり、多用な表現が可能になると思います。
プロトタイプベースのプログラミングは、便利なようで無駄なようで、イマイチ効果が実感できません。メソッド内のthisの扱いに四苦八苦して、グローバル変数を定義したい欲求をこらえながらどうにか完成すると、難しいパズルを解くような充足感はありました。
googleガジェットは、HTMLとJavaScriptの知識があれば簡単に作れるXMLファイルです。ちょっとしたアプリを作ってgoogleに登録しておけば、どこかで誰かが使うかもしれません。
上記ガジェットのシンプル版です。12桁制限を外してあります。
指定時間になるとアラームを鳴らすガジェットです。Cookieを使って、ブラウザのリロードに対応してあります。『JavaScript 第3版』の359ページに、Cookieを楽に操作するためのクラスが掲載されていたので、微妙に手を加えて引用しました。
数値と文字列を明示的に変換するテクニックです。
var num = 10; alert(typeof num); // numberと表示される var str = num + ""; alert(typeof str); // stringと表示される num = str -0; alert(typeof num); // numberと表示される
setIntervalの引数にメソッドを直接入れることはできません。たとえば次のようなコードでは、エラーになってしまいます。
function Foo() {
this.message = "hello";
}
Foo.prototype.alertOnce = function() {
alert(this.message);
}
Foo.prototype.alertForever = function() {
this.alertOnce();
var myId = setInterval(this.alertOnce(), 2000); // 間違い
}
var bar = new Foo();
bar.alertForever(); // エラーが発生
thisが期待どおりに解釈されないため、これでは正常に動作しません。そこでAjaxライブラリprototype.jsのbind()メソッドを利用して、thisの値を固定します。setIntervalの引数をthis.alertOnce.bind(this)にすると解決できます。このテクニックは上記の電卓ガジェットとタイマーガジェットでも使用しています。
<script src="prototype.js" type="text/javascript"><script>
Foo.prototype.alertForever = function() {
this.alertOnce();
setInterval(this.alertOnce.bind(this), 2000);
}
prototype.jsは、Ajaxに限らず、JavaScriptを楽に書くためのショートカットが用意されているので、JavaScriptの基礎をマスターしているのなら、使ってみるのもよいと思います。
JavaScriptでは、関数を次の4通りの方法で定義できます。
それぞれの使い方を示します。
// function文
function f(x, y) { return x + y; }
// Function()コンストラクタ
var f = new Function("x", "y", "return x + y");
// 関数リテラル
var f = function(x, y) { return x + y; };
// メソッド
o = new Object;
o.f = function(x,y) { return x + y; };
一度しか使わないような匿名関数を定義するときは、関数リテラルを使うのがよいでしょう。Function()コンストラクタも同等の機能を持ちますが、関数リテラルのほうがコードが読みやすくなります。
メソッドは、オブジェクトのプロパティに匿名関数を格納することで実現しています。
onclickやonchangeなどのイベントハンドラをHTMLに記述すると、HTMLにJavaScriptが混ざってしまい、文書構造が煩雑になってしまいます。HTMLファイルにはHTMLのみを記述して、JavaScriptとCSSは外部ファイルに分離するのが望ましいので、イベントハンドラを外部ファイルに記述する方法を紹介します。
次のHTMLでイベントハンドラを分離してみましょう。
<html> <head> <script type="text/javascript" src="script.js"></script> </head> <body> <form name="my_form"> <input type="button" name="my_button" value="foo" /> </form> </body> </html>
このHTMLにはmy_formフォームのmy_buttonボタンがあり、外部JavaScriptファイルscript.jsを読み込んでいます。my_buttonボタンを押すとalert関数が呼び出されるようにするには、script.jsに次のように記述します。
window.onload = function() {
document.my_form.my_button.onclick = function() {
alert("Hello.");
};
};
onloadイベントハンドラで、onclick時の動作を設定します。これで、HTMLからイベントハンドラを完全に分離できました。若干コードが複雑になりますが、ソースコードの可読性や保守性はよくなると思います。