AJAX では、JavaScript の異なる機能を使用する方法が多数あります。このドキュメントでは、AJAX コンポーネントを作成している開発者を対象に、クライアントで JavaScript を使用し、サーバーで Java を使用する際の推奨事項を示します。これらの推奨事項の多くは、ほかのサーバー側の技術にも当てはまります。推奨事項は次のとおりです。
element.innerHTML
を使用する際に注意する Java と同様に、特に大きな組織では、スタイルを標準化するとコードの操作と保守が簡単になります。Dojo が、優れた JavaScript プログラミング規約を定めています。
JavaScript もオブジェクト指向で記述することができます。オブジェクト指向にすると、コードを再利用しやすくなり、オブジェクトを体系化でき、オブジェクトを動的に読み込むことができます。ショッピングカートの JavaScript 版を次に示し、そのあとに同じ処理を行う Java コードを示します。
function Cart() {
this.items = [];
}
function Item (id,name,desc,price)) {
this.id = id;
this.name = name;
this.desc = desc;
this.price = price;
}
// カートのインスタンスを作成し、品目を追加する
var cart = new Cart();
cart.items.push(new Item("id-1","paper","something you write on",5));
cart.items.push(new Item("id-1","Pen", "Something you write with", 3);
var total;
while (var l; l < cart.items.length; l++) {
total = total + cart.items[l].price;
}
前出の Cart
オブジェクトは、Item
オブジェクトの内部配列を維持するための基本サポートを提供します。同じ処理を行うカートの Java オブジェクト表現は次のようになります。
import java.util.*;
public class Cart {
private ArrayList items = new ArrayList();
public ArrayList getItems() {
return items;
}
}
public class Item {
private String id;
private String name;
private String desc;
private double price;
public Item (String id, String name, String desc, double price) {
this.id = id;
this.name = name;
this.desc = desc;
this.price = price;
}
public String getId() {return id;}
public String getName() {return name;}
public String getDesc() {return desc;}
public float getPrice() {return price;}
}
前出の例は、カートのサーバー側オブジェクト表現を表します。このオブジェクトは、JSP、サーブレット、または JSF の管理対象 Bean によって HttpSession
内に保持する必要があります。AJAX の対話を使用して Cart に Item を追加したり、現在の状態を取得したりできます。
JavaScript では、オブジェクト名が重複する可能性があります。Java には、パッケージ名を使用して名前の重複を防ぐメカニズムがあります。JavaScript には Java のようなパッケージ名はありませんが、自分で名前の重複を防ぐことができます。名前の重複を防ぐには、コンポーネントを作成するときにオブジェクトとオブジェクト階層を使用して関連オブジェクトを体系化します。次に示す例では、最上位オブジェクト BLUEPRINTS
を作成しています。このオブジェクトは、関連オブジェクトの名前空間のように機能します。オブジェクトは、親オブジェクトのプロパティーとして設定しています。
// BLUEPRINTS 基本オブジェクトがない場合は作成する
if (!BLUEPRINTS) {
var BLUEPRINTS = new Object();
}
BLUEPRINTS.Cart = function () {
this.items = [];
this.addItem = function(id) {
items.push(new Item(id);
}
function Item (id,qty) {
this.id = id;
this.qty = qty;
}
}
// カートのインスタンスを作成し、品目を追加する
var cart = new BLUEPRINTS.Cart();
cart.addItem("id-1",5);
この方法で名前の重複を防ぐことができます。名前が重複する可能性がある場合にはこの方法を使用することをお勧めします。
prototype プロパティーは JavaScript の言語機能です。このプロパティーはすべてのオブジェクトに使用できます。JavaScript では、現在のオブジェクトにプロパティーがなかった場合、プロパティーが解決されるときに、prototype プロパティーの値が確認されます。プロトタイプオブジェクトとして定義されているオブジェクトの値にプロパティーがない場合、その prototype プロパティーの値が確認されます。JavaScript オブジェクトを継承させるには、多くの場合、prototype プロパティーの連鎖 (階層) を使用します。prototype プロパティーを使用して既存のオブジェクトに動作を追加する方法を次の例に示します。
function Cart() {
this.items = [];
}
function Item (id,name,desc,price)) {
this.id = id;
this.name = name;
this.desc = desc;
this.price = price;
}
function SmartCart() {
this.total;
}
SmartCart.prototype = new Cart();
SmartCart
は、Cart
オブジェクトを拡張してそのプロパティーを継承し、また total プロパティーを追加しています。このあとで、品目を追加できるようにする簡易関数と合計額を計算する簡易関数を Cart
に追加します。これらの関数は SmartCart
オブジェクトに直接追加することもできますが、この方法では SmartCart
のインスタンスごとに新しい関数が作成されます。JavaScript では、関数はオブジェクトなので、多数のインスタンスが作成されるオブジェクトでは、動作のインスタンスを共有できればリソースを節約できます。次のコードでは、共有の calcualteTotal
関数と addItem
関数を宣言し、これらの関数を SmartCart
のプロトタイプメンバーのプロパティーとして追加しています。
Cart.prototype.addItem = function(id,name,desc,price) {
this.items.push(new Item(id,name,desc,price));
}
Cart.prototype.calculateTotal = function() {
for (var l=0; l < this.items.length; l++) {
this.total = this.total + this.items[l].price;
}
return this.total;
}
// カートのインスタンスを作成し、品目を追加する
var cart = new SmartCart();
cart.addItem("id-1","Paper", "Something you write on", 5);
cart.addItem("id-1","Pen", "Soemthing you write with", 3);
alert("total is: " + cart.calculateTotal());
この例からわかるように、オブジェクトの拡張は簡単です。この例では、calcualteTotal
のインスタンスが 1 つあります。addItem
関数は、SmartCart
オブジェクトのすべてのインスタンスで共有されます (Java の静的メソッドに似ています)。関数を SmartCart
オブジェクトとは別に宣言しても、品目のスコープは変わらず this.items
です。新しいプロトタイプ関数が実行されるとき、SmartCart
オブジェクトのスコープになります。
オブジェクトのインスタンスが多数作成される可能性がある場合には、prototype プロパティーを使用して共通の動作を定義することをお勧めします。prototype プロパティーを使用すると、JavaScript 内のオブジェクト数を減らすことができ、また動作とデータを分離することができます。prototype プロパティーは、オブジェクトのデフォルトを指定するときにも役立ちます (ここではその方法は説明しません)。このドキュメントでは説明しませんが、prototype プロパティーは、ほかにもさまざまなことに使用できます。詳細は、「リソース」の節に示すドキュメントを参照してください。
前の例では、Cart をサーバー側オブジェクトにする方法とクライアント側オブジェクトにする方法の両方を示しました。状態をクライアントに保存するか、サーバーに保存するかは難しい問題です。JavaScript クライアントを単一ページのアプリケーションとして設計する場合は、カートをクライアントだけに置くほうが適切である可能性があります。この場合、JavaScript のカートは、チェックアウト時にのみサーバーと対話します。
一般に、特定のページに関連するビューの状態は JavaScript オブジェクトを使用して保存します。JavaScript オブジェクトは HTML ページに固有なので、「再読み込み」ボタンをクリックするか、ブラウザを再起動するか、ブラウザがクラッシュするか、別のページを表示すると失われます。
複数のページで使用する状態は、サーバー上に HttpSession
スコープの Java オブジェクトとして保存します。クライアントとサーバーのオブジェクトは、ページの更新時や読み込み時に同期する必要があります。
たとえば外出中にオフラインで機能する必要がある AJAX クライアントを開発する場合は、クライアントに状態を保存することもできますが、これは標準的な方法ではありません。Dojo は、JavaScript クライアントでオフラインで使用できる 100K バイト以内の内容を保存する dojo.storage
API を提供しています。クライアント側での保存が標準化されたときには、標準に準拠するためにこの API が採用されます。保存する状態が機密である場合や、複数のコンピュータから状態にアクセスする必要がある場合は、サーバーに状態を保存することを検討します。
JavaScript は、必要な場合以外は特定のコンポーネントに関連付けないようにします。パラメータを使用できる関数にデータをハードコーディングしない方法を検討します。再利用可能な自動補完の JavaScript 関数を次の例に示します。
<script type="text/javascript">
doSearch(serviceURL, srcElement, targetDiv) {
var targetElement = document.getElementById(srcElement);
var targetDivElement = document.getElementById(targetDiv);
// serviceURL と srcElement.value に基づいて補完内容を取得する
// targetDiv 要素の内容を更新する
}
</script>
<form onsubmit="return false;">
Name:<input type="input" id="ac_1" autocomplete="false"
onkeyup="doSearch('nameSearch','ac_1','div_1')">
City:<input type="input" id="ac_2" autocomplete="false"
onkeyup="doSearch('citySearch','ac_2','div_2')">
<input type="button" value="Submit">
</form>
<div class="complete" id="div_1"></div>
<div class="complete" id="div_2"></div>
前出の例の doSearch()
関数は、要素の文字列 ID、サービス URL、および更新する <div> がパラメータになっているので、再利用が可能です。このスクリプトは、別のページまたはアプリケーションで使用できます。
オブジェクトリテラルは、中括弧 ({}) 内にコンマで区切ったキーと値のペアを指定して定義するオブジェクトであり、Java のマップに似ています。
{key1:"stringValue", key2:2, key3:['blue','green','yellow']}
前出の例は、文字列、数値、および文字列配列を含むオブジェクトリテラルを示します。オブジェクトリテラルは、関数に引数を渡す一般的な方法として使用できる点が便利です。関数内で必要なプロパティー数を増やしても、関数シグニチャーは変わりません。オブジェクトリテラルをメソッドのパラメータとして使用することを検討してください。
function doSearch(serviceURL, srcElement, targetDiv) {
var params = {service:serviceURL, method:"get", type:"text/xml"};
makeAJAXCall(params);
}
function makeAJAXCall(params) {
var serviceURL = params.service;
...
}
また、オブジェクトリテラルでは、次の例のように匿名関数を渡すこともできます。
function doSearch() {
makeAJAXCall({serviceURL:"foo",
method:"get",
type:"text/xml",
callback:function(){alert('call done');}
});
}
function makeAJAXCall(params) {
var req = // getAJAX 要求;
req.open(params.serviceURL, params.method, true);
req.onreadystatechange = params.callback;
...
}
オブジェクトリテラルは、構文が似ている JSON と混同しないでください。オブジェクトリテラルについては、「リソース」の節に示すドキュメントを参照してください。
ここでいう「圧縮」とは、zip 形式の圧縮スキームとは異なり、ファイル内の変数名や関数名の空白を削除し、名前を短縮することです。アプリケーションを配備するときに JavaScript のリソースを圧縮することを検討します。開発モードでは、デバッグしやすいようにスクリプトを読みやすくしておきます。他社の JavaScript ライブラリを使用する場合、圧縮版があればそれを使用します。スクリプトを圧縮すると、帯域幅を大幅に節約できます。たとえば、Dojo 0.2.2 では、圧縮版 dojo.js は 130K バイトで、非圧縮版は 208K バイトです。Dojo は、JavaScript ファイルを簡単に圧縮できる ShrinkSafe という圧縮サービスを提供しています。
サーバー側コンポーネントから jar ファイルでスクリプトやスタイルを配布する場合は、jar ファイルの作成時に zip 形式の圧縮を使用しないでください。zip 形式で圧縮すると、クライアントからスクリプトまたはリソースが要求されるたびにサーバーでファイルを圧縮解除する必要があります。スクリプトが大きいと、サーバーでパフォーマンスの問題が発生する可能性があります。
ビジネスロジックやサーバー側のアクセスコードを JavaScript に入れないようにします。たとえば、#{SomeBean.someMethod} のような JSF のメソッドと値のバインド式や、サーバーで呼び出すクラスやメソッドの名前を公開するときには注意してください。公開すると、内部ドメインモデルが公開されるので、悪用される可能性があります。このようなメカニズムを使用する場合は、JavaScript クライアントから、クライアントを対象としたメソッドだけを呼び出せるようにする必要があります。SQL 文は JavaScript コードに入れないでください。JavaScript コードは、ボタンを 1 つクリックするだけでクライアントで表示できます。
要求の送信元が AJAX クライアントであるかどうかにかかわらず、必ずサーバーで要求のパラメータを検証します。 大きなライブラリまたはライブラリセットがある場合、ページの読み込み時にすべてを読み込む必要はありません。JavaScript は、JSAN などのライブラリを使用して実行時に動的に読み込むことができます。また、AJAX を使用して手動で JavaScript コードを読み込み、JavaScript の eval()
を呼び出すこともできます。クライアントで動的に読み込まれ、Cart
オブジェクトのインスタンスを作成する JavaScript コードの例を次に示します。
cart.js
function Cart () {
this.items = [];
this.checkout = function() {
// チェックアウトのロジック
}
}
ここで、コードで cart.js のテキストを評価し、Cart
オブジェクトを作成します。
// AJAX 要求を使用して cart.js を取得
eval(javascriptText);
var cart = new Cart();
// カートに品目を追加
cart.checkout();
javascriptText
で定義されているオブジェクトは、eval(javascriptText);
を呼び出したコンテキストまたはスコープだけで使用できます。
AJAX でのモデルデータのトランスポートには XML 形式がまだ有効ですが (特に XML ベースのサービスと通信する場合や、サービスが AJAX ベース以外のクライアントも対象とする場合)、サーバーから JavaScript ベースのクライアントへのデータの送信には JSON を使用することを検討します。次の XML ドキュメントは、それぞれ 3 つの製品が含まれる 2 つの製品カテゴリを表しています。
<categories>
<category id="0" name="Vegetables">
<products>
<product>
<name>Onion</name>
<price>.75</price>
</product>
<product>
<name>Carrot</name>
<price>.50</price>
</product>
<product>
<name>Eggplant</name>
<price>1.50</price>
</product>
</products>
</category>
<category id="1" name="Fruit">
<products>
<product>
<name>Orange</name>
<price>1.25</price>
</product>
<product>
<name>Apple</name>
<price>1.30</price>
</product>
<product>
<name>Tomato</name>
<price>.40</price>
</product>
</products>
</category>
</categories>
前出の XML ドキュメントでは、クライアントで XML ドキュメントの DOM を確認し、データを JavaScript のオブジェクト表現にバインドするために必要な JavaScript コードの量が多くなります。カテゴリ要素と製品要素のオブジェクト定義、およびその関係をコードで定義する必要があります。このドキュメントを解析し、オブジェクト表現にバインドするのに必要な JavaScript を次の例に示します。この例では、Category
オブジェクトの配列を使用しています。各オブジェクトは Product
オブジェクトの配列を含んでいます。
function Category (id, name) {
this.id = id;
this.products = [];
}
function Product (name,price) {
this.name = name;
this.price = price;
}
var categories = [];
// AJAX 要求から返された XML を使用したコールバック
function processResults(responseXML) {
var categoryElements = responseXML.getElementsByTagName("category");
for (var l=0; l < categoryElements.length; l++) {
var categoryElement = categoryElements[l];
var catId = categoryElement.getAttribute("id");
var catName = categoryElement.getAttribute("name");
var category = new Category(catId, catName);
var products = categoryElement.getElementsByTagName("product");
for (var pl=0; pl < products.length; pl++) {
var prodName = products[pl].getElementsByTagName("name")[0].firstChild.nodeValue;
var prodPrice = products[pl].getElementsByTagName("name")[0].firstChild.nodeValue;
category.products.push(new Product(prodName,prodPrice));
}
categories.push(category);
}
var veggies = categories[0].products[0];
}
前出の例からわかるように、クライアント側で XML ドキュメントをデータモデルに変換する処理が必要です。
JSON は、データ、オブジェクト定義、および関係をプレーンテキストで定義する JavaScript のネイティブの単純な形式です。次のコード例では、JSON を使用して、前述の XML ドキュメントと同じデータを表しています。
[
{"id":"0", "name":"Vegetables", "products":
[{"name":"Onion", "price":.75},
{"name":"Carrot", "price":.50},
{"name":"Eggplant", "price":1.50} ]},
{"id":"1", "name":"Fruit", "products":
[{"name":"Orange", "price":1.25},
{"name":"Apple", "price":1.30},
{"name":"Tomato", "price":.40} ]},
]
Product
オブジェクトの配列を含む Category
オブジェクトの配列を作成するには、AJAX 呼び出しを使用して JSON をプレーンテキストで取得し、JavaScript の eval()
関数を使用して変数を割り当てます (この例では categories
)。
var jsonText = // 前出の JSON テキストを取得
var categories = eval(jsonText);
var veggies = categories[0].products[0];
これで完了です。クライアント側で XML を処理するコードは必要ありません。ただし、サーバー側ロジックでデータを JSON 形式で表す必要があります。
優れた Web アプリケーションのユーザーインタフェースは、内容 (HTML/XHTML)、スタイル (CSS)、および JavaScript から構成されます。JavaScript は、マウスのクリックなどユーザーの操作によって起動され、内容を操作できるという点で重要です。CSS のスタイルと JavaScript を分離すると、コードが管理しやすく、読みやすく、またカスタマイズしやすくなります。CSS と JavaScript はファイルを分けることをお勧めします。
JSP、サーブレット、JSF レンダリングなどの Java ベースのコンポーネントから HTML を表示する場合は、スタイルと JavaScript を次のように各ページに出力したくなるかもしれません。
<style>内容を各ページに埋め込むと、ページが読み込まれるたびに使用帯域幅が増える可能性があります。JavaScript と CSS を埋め込むのではなく、次のように参照すると、これらのファイルをキャッシュし、ページ間で再利用できます。
#Styles
</style>
<script type="text/javascript">
// JavaScript logic
<script>
<body>The quick brown fox...</body>
<link rel="stylesheet" type="text/css" href="cart.css">
<script type="text/javascript" src="cart.js">
<body>The quick brown fox...</body>
サーバー上の静的リソース、またはリソースを動的に生成する JSP、サーブレット、または JSF コンポーネントにリンクを対応付けることができます。JSF コンポーネントを開発する場合は、Shale Remoting を使用することを検討してください。これには、スクリプトと CSS のリンクを記述するための基本機能と、JSF コンポーネントの jar ファイルからも提供できるリソースへのアクセスを提供する基底クラスがあります。
JavaScript コードで要素のスタイルを動的に変更する場合は、element.setAttribute("class", STYLE_CLASS)
ではなく element.className
を使用します。element.className
は、もっとも多くのブラウザでサポートされています。また、IE でスタイルの変更をサポートするもっとも効果的な方法でもあります。
JavaServer Faces (JSF) のタグライブラリ定義と Java のタグライブラリでは、class 属性を使用してスタイルを指定できません。すべての Java オブジェクトに getClass()
メソッドがあり、このメソッドはオーバーライドできないからです。スタイルの指定には属性名 styleClass を使用します。
ほかのソースコードと同様に、JavaScript 内の静的な HTML または XHTML の内容は最小に保ってください。これによって、静的内容を更新するソースの管理が容易になります。コンポーネントの内容を、ソースを変更しないで更新可能にしたい場合もあるでしょう。静的内容は、ResourceBundles
のある Java で行われるのと類似した方法でコードから抽出され、AJAX 要求を使用して読み込まれます。このデザインは、ローカライズされたインタフェースを作成する手段として使用できます。
次の XML ドキュメント resources.xml
およびコードは、JavaScript コードからどのように静的リソースが抽出できるかを示しています。
<resources>
<resource id="foo">
<value>bar</value>
</resource>
<resource id="fooy">
<value>fooy</value>
<value>bar</value>
</resource>
</resources>
JavaScript 読み込み機能は、window.onload
機能にそれをマッピングすることによって、HTML ページで読み込まれます。
function load() {
//イメージの最初のセットを読み込み
//AJAX 要求を使用して resources.xml を取得
//processResults() } に responseXML を提供
function Resource (id, values) {
this.name = id;
this.value = values.join();
this.values = values;
}
var resources = new Object();
//AJAX 要求から返された XML の構文解析
function processResults(responseXML) { var resourceElements =
responseXML.getElementsByTagName("resource");
for (var l=0; l < resourceElements.length; l++) {
var resourceElement = resourceElements[l];
var id = resourceElement.getAttribute("id");
var valueElements = resourceElement.getElementsByTagName("value");
var values = [];
for (var vl=0; vl < valueElements.length; vl++) {
var value = valueElements[vl].firstChild.nodeValue;
values.push(value);
}
resources[id] =new Resource(id,values);
}
alert ("foo=" + resources['fooy'].value);
}
このコードは、前述の XML ドキュメントを構文解析し、リソースオブジェクト内に各リソースを配置します。JSON を使用して同じデータを送信できますが、ローカライズされた内容を送信するときは、それをサポートするための、より容易な手段が提供されるため、XML を選択するほうが望ましいといえます。「国際化を考慮して設計する」を参照してください。
GUI コンポーネントと同様に、静的内容が外部ソースから提供される可能性がある場合にレイアウトのサイズを静的に設定するときには注意してください。新しい内容がコンポーネントの境界を越える可能性があります。
element.innerHTML
を使用する際に注意するelement.innerHTML
は、DOM 式の element.appendChild()
よりもプログラミングが簡単で、また DOM API を使用するよりもブラウザでの処理が速いので、element.innerHTML
を使用したくなるかもしれません。しかし、この方法には短所があることを理解しておく必要があります。
element.innerHTML
を使用する場合は、最小限の HTML を生成する JavaScript を記述するようにします。表示を整えるには、CSS を使用します。内容と表示の分離は常に意識する必要があります。また、要素の既存の innerHTML
内のイベントリスナーの登録を解除してから、element.innerHTML
を再設定します。このようにしないとメモリーリークが発生します。element.innerHTML
内の DOM 要素は、内容を置き換えると失われ、またこれらの要素への参照も失われます。
JavaScript で要素を動的に作成する場合は、element.appendChild()
などの DOM API を使用することを検討します。DOM API は標準であり、要素の作成時に変数としてプログラムからアクセスでき、element.innerHTML
を使用したときに発生するメモリーリークや参照が失われるなどの問題を簡単に回避できます。ただし、DOM を使用して IE で表を作成すると問題が発生する可能性があります。IE の API は DOM に従っていないからです。MSDN にある表に関するドキュメントで必要な API を確認してください。IE で表以外の要素を追加しても問題は発生しません。
JavaScript では、Java のようなガベージコレクションが使用されますが、Java と同様に、ガベージコレクションがメモリー管理の総合的な解決策ではないことを覚えておく必要があります。変数が不要になったら参照を解除し、変数をガベージコレクションの対象にする必要があります。element.innerHTML
を使用する場合は、コード内のリスナーの参照を解除してから置き換えるようにします。
イベント処理コードは、メモリーリークが発生しやすくなっています。Dojo または MochiKit にあるイベントラッパーは、リークを防ぐように設計されているので、これらのラッパーを使用することを検討してください。
クロージャーは簡単に作成できますが、場合によっては問題を起こす可能性があります。オブジェクト指向の開発に慣れている開発者は、オブジェクトに関連する動作をオブジェクトにバインドする傾向があります。メモリーリークが発生するクロージャーの例を次に示します。
function BadCart() {
var total;
var items = [];
this.addItem = function(text) {
var cart = document.getElementById("cart");
var itemRow = document.createElement("div");
var item = document.createTextNode(text);
itemRow.appendChild(item);
cart.appendChild(item);
itemRow.onclick = function() {
alert("clicked " + text);
}
}
}
この例は何が問題なのでしょうか。addItem
の参照先が、BadCart
のスコープに含まれない DOM ノードになっています。匿名の onClick ハンドラ関数が itemRow
への参照を保持しているので、itemRow
を明示的に null
に設定しないと itemRow
はガベージコレクションの対象になりません。次に示す GoodCart
のコードで問題は解決します。
function GoodCart() {
var total;
var items = [];
this.addItem = function(text) {
var cart = document.getElementById("cart");
var itemRow = document.createElement("div");
var item = document.createTextNode(text);
itemRow.appendChild(item);
cart.appendChild(item);
itemRow.onclick = function() {
alert("clicked " + text);
}
itemRow = null;
item = null;
cart = null;
}
}
itemRow
を設定すると、itemRow.onclick
ハンドラとして設定した匿名関数による外部参照が削除されます。クリーンアップを行い、また item
と cart
を null
に設定すると、すべてがガベージコレクションの対象になります。BadCart
で匿名関数の代わりに外部関数を使用しないことでメモリーリークを防ぐこともできます。
クロージャーを使用する場合は、クロージャーのローカル変数を使用して DOM 関連オブジェクトなどのブラウザオブジェクトへの参照を保持しないように注意します。参照を保持すると、メモリーリークが発生する可能性があります。オブジェクトへの参照がすべてなくなるまで、オブジェクトはガベージコレクションの対象になりません。クロージャーについては、「Javascript Closures」(Javascript クロージャー) を参照してください。
JavaScript が中心の JSP、サーブレット、または JSF のコンポーネントによる依存スクリプト、CSS ファイル、画像などのリソースの読み込みをオーバーライドする手段を用意するようにします。コンポーネントを JSP タグまたは JSF コンポーネントとともに jar ファイルにまとめる場合は、これらのリソースファイルを WEB-INF ディレクトリに移動し、クライアントにストリーム出力するコードを作成します。JSF では、フェーズリスナーを使用してこの処理を行うことができます。その他の Java Web 技術では、サーブレットまたはフィルタを使用できます。このようにすると、コンポーネントの開発時にコンポーネントの再パッケージ化や配備を行わずにスタイルや JavaScript を簡単に変更できます。ユーザーは、バンドルを使用できるので、それぞれの Web アプリケーションへのリソースファイルの配備について心配する必要がありません。このソリューションによって、ユーザーはコンポーネントの再構築や再パッケージ化を行わずにスタイルや JavaScript を必要に応じてカスタマイズできます。
HTML ページのエンコーディングには、UTF-8 がもっとも多くの言語をサポートします。
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
ローカライズしたフォームのフィールド変数を渡すときは、JavaScript の encodeURIComponent()
関数を使用してフィールド変数を HTTP フレンドリな表示に変換します。エンコーディングには、HTML ページの内容の種類と同じエンコーディングが使用されます。次に示す例は、id が complete-field のフォームの入力フィールドの値をエンコードし、Dojo を使用して AJAX 要求を行う方法を示します。
var completeField = document.getElementById("complete-field");
var bindArgs = {
url:"autocomplete?action=complete&id=" + encodeURIComponent(completeField.value),
mimetype:"text/xml",
load:function(type, data) {
processSearch(data);
}
};
dojo.io.bind(bindArgs);
ローカライズされたデータを返す内容の種類として XML を使用することもできます。XML では、XMLHttpRequest オブジェクトで認識し、適用できるエンコーディング情報を指定できるからです。ローカライズされた内容をほかにも渡す場合は、返された内容をサーバーで正しくエンコードする必要があります。サーバーでの処理については、「どのようにして国際化された AJAX インタラクションを提供したらよいですか。」を参照してください。
http://dojotoolkit.org/docs/js_style_guide.html
オブジェクト指向の JavaScript の詳細について:
http://www.cs.rit.edu/%7Eatk/JavaScript/manuals/jsobj/index.htm名前空間のエミュレートについて:
http://www.justatheory.com/computers/programming/javascript/emulating_namespaces.htmlJSON について:
JavaScript のオブジェクトリテラルについて:
http://developer.mozilla.org/ja/docs/Core_JavaScript_1.5_Guide:Literals
JavaScript のクロージャーについて:
http://jibbering.com/faq/faq_notes/closures.html
JavaScript に関するさまざまなヒント:
CSS、内容、および JavaScript の分離について:
http://thinkingandmaking.com/entries/63