goo blog サービス終了のお知らせ 

DEVELOPMENTOR*LEARNING RESOURCE LAB.

ソフトウェア開発現場の視点からソフトウェアと開発者の価値を高める「ホットな何か」をお届けします。(休止)

prototype.jsのClassをパッケージ階層っぽく使う

2006年10月05日 | prototype cast
prototype.jsにはAjax.RequestやInsertion.Beforeのようにパッケージっぽい名前をもつクラスがあります。この「Ajax.」や「Insertion.」といったパッケージっぽいことを自前のクラスでも実現してみます。

次のソースコードはFooパッケージのBarクラスを定義したつもりですがエラーとなって動きません。
Foo.Bar = Class.create();
Foo.Bar.prototype = {
  initialize: function() {
  },
  baz: function(message) {
    alert(message);
  }
}

エラーの原因はFooが未定義のためです。ですので、次のようにFooを事前にオブジェクにしておきます。
var Foo = new Object();
Foo.Bar = Class.create();
Foo.Bar.prototype = {
  initialize: function() {
  },
  baz: function(message) {
    alert(message);
  }
}

これでエラーなく期待したとおりに動作します。
  var bar = new Foo.Bar();
  bar.baz('Are you ready?');

パッケージっぽいところは単なるオブジェクトだったのですね。(^^;

prototype.jsのElement.showで表示できないときは...

2006年10月05日 | prototype cast
次のような(非表示の)ブロック要素があり...
<div id="r">(ブロック要素)</div>

そのブロック要素を次のようにElement.showで指定しても表示できないという話題をよく見かけます。
Element.show('r');

意図しない振る舞いに「これはElement.showの不具合ではないか?」と立ち止まってしまいがちですが、prototype.jsのソースコードを調べるとその原因が見えてきます。

次の関数(メソッド)はElement.showのソースコードを抜粋したものです。ソースコードを見るとElement.showは指定した要素のdisplay属性を既定値(初期値)に戻しているようです。
  show: function() {
    for (var i = 0; i <arguments.length; i++) {
とすると、Element.showで指定した要素が表示できないときは、次のようなスタイルを指定していると推測できます。このスタイルだとElement.showとしても結果としてdisplay属性がnone(非表示)となってしまいます。
#r {
  color: red;
  display: none;
}

ですので、あらかじめ要素を非表示としておいてElement.showで表示をするときは、その要素のインラインスタイルでdisplay属性をnoneとし、スタイルではdisplay属性を指定しないという方法が1つの解決策になります。
#r {
  color: red;
  /*display: none;*/
}

<div id="r" style="display: none;">(ブロック要素)</div>



prototype.jsのObject.extendで関数の省略値を補完する

2006年10月05日 | prototype cast
関数(メソッド)の引数を省略可とし、省略したとき何らかの初期値を補完したいことがよくあります。ただJavaScriptは関数のオーバーロードができないので... 関数の引数が省略されたかチェックし、省略されたときは初期値を補完するといったコードを起こす必要があります。

巷のライブラリは、省略可なパラメータを1つの引数にまとめることが多いようです。次の関数の例は「a1」「a2」は必須「options」を省略可としています。
また「options」は引数を省略可とし、さらに「oprions.a」「oprions.b」「oprions.c」のいずれも省略可としています。
function f(a1, a2, options) {
  var r = Object.extend({
    a: 123,
    b: 'abc',
    c: 'xyz'
  }, options || {});
}

上記の関数は次のように様々な呼び出し方ができます。このとき省略した「options」もしくは「options.a」「options.b」「options.c」は、あらかじめ指定しておいた初期値で期待どおり補完されます。
  f('#1', '#2');
  f('#1', '#2', {});
  f('#1', '#2', {a: 456});
  f('#1', '#2', {a: 456, b: 'def'});
  f('#1', '#2', {a: 456, b: 'def', c: 'XYZ'});


prototype.jsのEnumerable.findでラジオボタンの値を取得する

2006年10月05日 | prototype cast
次のようなラジオボタンを使ったフォームはよく使います。
<form name="f">
<input type="radio" name="i" value="123" checked>
<input type="radio" name="i" value="abc">
<input type="radio" name="i" value="xyz">
</form>

ただJavaScriptから選択されたラジオボタンの値を取得するのはちょいと面倒だったりします。
  var r = null;
  for (var i=0; i<document.f.i.length; i++) {
    if (document.f.i[i].checked) {
      r = document.f.i[i];
      break;
    }
  }
  alert(r.value);

次のようにEnumerableオブジェクトのfindメソッドを使えば簡潔になります。
  var r = $A(document.f.i).find(function(v) {
    return v.checked;
  });
  alert(r.value);

Ajax.InPlaceEditorで任意のパラメータをPOSTする

2006年10月05日 | prototype cast
script.aculo.usのAjax.InPlaceEditorでデータを送信するとき、識別用のIDといった任意のパラメータを付与するサンプルがWeb2.0note script.aculo.usの使い方(InPlaceEditor) その2で公開されています。

サンプルはGET(POSTではなく)でデータ送信するという制約があるので、該当ページのコメントをヒントにcallback関数を使ってPOSTでデータ送信するサンプルを用意しました。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="effects.js"></script>
<script type="text/javascript" src="controls.js"></script>
</head>
<body>
<h1 id="callback">(callback)</h1>
<script type="text/javascript">
<!--
new Ajax.InPlaceEditor($('callback'),
  'inplaceeditorcallback.cgi', {
  callback: function(form) {
    var id = document.createElement('input');
    id.type = 'hidden';
    id.name = 'id';
    id.value = 'ABC12345';
    form.appendChild(id);
    return Form.serialize(form);
  },
  ajaxOptions: {method: 'post'}
});
-->
</script>
</body>
</html>


callback関数はデータ送信する直前に呼び出されます。callback関数の戻り値は「Ajax.Request parameters:」の値となります。callback関数の引数には送信フォームが渡されます。この送信フォームはAjax.InPlaceEditorの内部(隠し?)フォームでデータ送信の元ネタとなっています。

サンプルはこの特性を利用してAjax.InPlaceEditorで任意のパラメータをPOSTしています。callback関数の中で識別用のIDパラメータ(隠しフィールド)を送信フォームに追加し、その後「Ajax.Request parameters:」の値となるように送信フォームをシリアライズしています。

Ajax.InPlaceEditorの詳しい仕様はAjax.In Place Editor in scriptaculous wikiを参照してください。ちなみにForm.serialize(prototype.js)はパラメータ値をencodeURIComponentでエンコードするようです。

データ受信は次のスクリプトを使いました。

#!/usr/bin/perl
use CGI;
my $cgi = new CGI;
print $cgi->header(-type=>'text/plain');
print $cgi->request_method;
print $cgi->save(*STDOUT);
"True Value";


href="javascript:..."とonclick="..."の違い(その1)

2006年10月04日 | prototype cast
javascriptでaタグのクリックイベントをハンドリングする方法は2通りあります。「href="javascript:..."」のようにhref属性を使う方法と「onclick="..."」のようにonclick属性を使う方法です。

javascriptは用途と限定して使うことが多いため気づきにくいのですが、href属性とonclick属性は振る舞いが異なります。次のスクリプトを実行してみるとその違いがわかります。

<script language="javascript">
<!--
function go1(text) {
  alert(text);
}
-->
</script>
<a href="javascript:void(0);" onclick="go1('%35%35');">(onclick)</a>
<a href="javascript:go1('%35%35');">(href)</a>


onclick属性のときは「%35%35」の文字列がそのままメッセージボックスに表示されます。href属性のときは「%35%35」の文字列ではなく「%35%35」をデコード(アンエスケープ)した「55」の文字列がメッセージボックスに表示されます。文字コードの「35(16進数)」は文字の「5」に該当します。



これはとても大きな違いです。私がWebアプリケーションを開発するときはonclick属性を使うことを基本としています。ただし、指定する文字列が限定され、前述の振る舞いに不都合がないときはhref属性を使うこともあります。

もう1つhref属性とonclick属性の振る舞いの違いがあります。その違いは後日ご紹介します。