データバインド
データバインド
データバインド入門
Table of contents
PART1 データバインドの基本
■データバインドとは何か ■まずはシンプルな例から ●データベースを可能にするおまじない ●DATASRCとDATAFLD ●用語解説を兼ねたまとめ ■表示するレコードを切り替える ■recordsetオブジェクトのプロパティとメソッドのまとめ
PART2 繰り返しテーブル
■繰り返しテーブルを作る ■繰り返しテーブルに見出しを付ける ■一度に表示するレコード数を指定する
PART3 データコンシューマ
■データコンシューマいろいろ ■データバインド拡張機能とデータコンシューマ
PART4 並び替えと絞込
■レコードの並び替え ■レコードの絞込
PART5 補足情報
■Tabular Data Controlのプロパティ ■ヘッダー行がない場合の処理
PART6 スクリプト作成に役立ついくつかの実験
■recordNumberプロパティは繰り返しテーブルの表示順 ●並び替え、カレントレコード、recordNumberプロパティの関係 ●絞込、カレントレコード、recordCountプロパティ、recordNumberプロパティの関係 ●nextPage()メソッド、previousPage()メソッド、カレントレコード、recordNumberプロパティ、recordCountプロパティの関係
PART7 活用してみよう
■中古車データベースのデモ ■7桁郵便番号検索~東京編 ■より高度なトピック
PART1 データバインドの基本
この記事では、InternetExplorer4.0(以下、IE4)のDynamicHTMLの機能の1つであるデータバインド(データ連結)の概要を紹介する。本来はDynamicHTMLの基本から始めるべきだが、ここでは割愛する。なぜかというと、それはちょっと面倒で骨が折れるから。で、どうして「データバインドなのか」というと、これがIE4のDynamicHTMLの中で、けっこう重要なポジションを占めるだろうと思ったからだ。同時に、個人的にも自分が得た知識をまとめておきたい気持ちがあったからなのだ。
読者に求められる知識は、HTML、JavaScript、DynamicHTML、スタイルシート(CSS1)の4つ。「ああ、じゃ読んでも分からないや」とココで読むのをやめてもいいが、そのすべてを理解している必要はない。そもそも、書いてる本人も、ぜんぶ分かっているわけじゃない。どれも、ちょっとだけなら分かります、という程度なら大丈夫だ(分からなかった調べよう)。
なお、「データバインド」はIE4のDynamicHTMLの中では、"難しい部類"というか、IE4のDynamicHTML関連本では後ろの方に出てくる内容、といったところ。だから、DynamicHTMLについてひととおり勉強してから取り組む内容、と思われがちだが、必ずしもそうとはかぎらない。先にデータバインドやっても、問題ないと思う。まぁ、本を書く立場の人間からすると、構成上&商売上そうせざるをえないのは分かるけれども、それはそれとして、「データバインド」、なかなか面白い。DynamicHTMLを熟知しなくても、データバインドは十分利用できる、ということを強調したうえで、スタート。
■データバインドとは何か
「データバインド」とは何か? と聞かれたら、ボクは「Webページをデータベースソフトに変えるマジック」と答える。と書いても、ナンノコッチャ? と思うのが普通なので、まずはそのマジックをお見せしたい。
このページは、データバインドを利用して作ったWebページだ。「住所1」「住所2」の入力欄に文字を入力し、<検索>ボタンをクリックすれば、入力した文字を条件とした絞込が実行される。たとえば、「都道府県」で「東京都」を選び、「住所1」に「練馬区」と入力して<検索>ボタンをクリックすれば、条件に合致するデータだけが絞り込まれて表示される。データベースソフトを使ったことのある方なら、何をやってるかは分かるだろう。「都道府県」を選択したときデータの読込に若干の時間がかかるが、一度読み込んだ都道府県のデータはキャッシュに入るため、2回目以降の読み込みは高速になる。
「データバインド」を使えば、Webページでこうした処理が可能になる。処理そのものに目新しさはないが、ポイントはこれがWebページで実現されていることだ。しかも、サーバ側ではCGI等を使った処理はいっさいしていない。すべてクライアント側(IE4)で処理が行われる。
では、もっとシンプルな例を使って、データバインドの具体例を紹介していこう。
■まずはシンプルな例から
「データバインド」では、HTMLファイルとテキスト形式のデータファイルの2つのファイルが重要な役割を演じる。まずは、その2つのファイル例を紹介しよう。
【サンプル1:smp001.html】
HTML>
HEAD>
TITLE>サンプル1</TITLE>
/HEAD>
BODY> <P ALIGN=center>
SPAN ID="span1" DATASRC="#carData" DATAFLD="name"></SPAN><BR>
SPAN ID="span2" DATASRC="#carData" DATAFLD="maker"></SPAN><BR>
SPAN ID="span3" DATASRC="#carData" DATAFLD="color"></SPAN><BR>
SPAN ID="span4" DATASRC="#carData" DATAFLD="year"></SPAN><BR>
SPAN ID="span5" DATASRC="#carData" DATAFLD="price"></SPAN>
/P>
!-- データソースオブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
【car.txt】
name,maker,color,year:Int,price:Int
アコード,ホンダ,白,92,800000
マークⅡ,トヨタ,白,94,1500000
カムリ,トヨタ,黒,90,550000
サニー,ニッサン,シルバー,96,1200000
MPV,マツダ,赤,91,1350000
デミオ,マツダ,濃紺,95,1400000
シビック,ホンダ,青,93,1100000
シーマ,ニッサン,白,91,2100000
ゴルフ,フォルクスワーゲン,シルバー,94,1700000
シビック,ホンダ,シルバー,92,890000
カローラ,トヨタ,白,92,550000
アコード,ホンダ,濃紺,93,1200000
ステップワゴン,ホンダ,赤,96,1300000
ステップワゴン,ホンダ,白,97,1600000
スカイライン,ニッサン,シルバー,97,2000000
プリメーラ,ニッサン,白,93,650000
ユーノスロードスター,マツダ,青,94,1250000
カローラ,トヨタ,白,90,450000
ゴルフ,フォルクスワーゲン,黒,97,2100000
以上の2つのファイルをサーバ上の同じディレクトリに置く。そして、ブラウザを使ってcar001.htmlを表示する(ローカルのハードディスクの同一フォルダに置いてcar001.htmlを読み込んでも同じ)。実際に上のサンプル1のリンクをクリックして表示してみよう。さて、ここでは何が起きているだろうか。表示されるデータは、次のとおり。
アコード
ホンダ
白
92
800000
つまり、car.txtの2行目のデータが表示されている。car.txtは、表計算ソフトやデータベースソフト等で作成できる単純なカンマ区切りのテキストファイルだ。データをカンマ(,)で区切り、1行が1件分(1レコード)に相当する。ある程度パソコンを使ったことのある方には、おなじみの形式だと思う。
これだけだと、レコードが1件表示されるだけで面白くもないのだが、"カンマ区切り形式のテキストファイルに書かれているレコードがHTMLファイル中に出力されている"という事実は、けっこうインパクトがある。ここで、2つのファイルの重要な記述を押さえておきたい。
COLUMN テキストファイルの項目名
テキストファイルとして用意したcar.txtの1行目が次のようになっていることに注目してほしい。
name,maker,color,year:Int,price:Int
いわゆる項目名の指定だが、year:Int,price:Int とちょっと変わった指定がある。これは、year項目とprice項目のデータが整数値であることを示している。いまはあまり気にする必要はないが、この指定は並び替えや絞込で重要な意味を持ってくる。詳細は後述する。
●データベースを可能にするおまじない
まずは、HTMLファイル(car001.html)の次の記述に注目してほしい。
!-- データソースオブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
これは、いわば"おまじない"の部分だ。<OBJECT>の属性として、IDに「carData」、CLASSIDに「clsid:333C7BC4-460F-11D0-BC04-0080C7055A83」と指定している。特にCLASSIDについては、深く考える必要はない。このように記述するものと決まっている。重要なのは、IDに「carData」を指定していること。これによって、Webページに「carData」という名前のデータソースオブジェクトが生成される。ナンノコッチャ、だと思うが、あまり深く考えることはない。この部分に「carData」という名前をつけたと思えばいい。
次にパラメータの部分を1つずつ見てみよう。
PARAM NAME="DataURL" VALUE="car.txt">
これは、VALUE属性に「car.txt」を指定しているのがポイント。「NAME="DataURL"」は固定だ。「car.txt」としたことで、データファイルに「car.txt」を指定したことになる。
PARAM NAME="UseHeader" VALUE="true">
これは、指定したcar.txtの1行目がヘッダー行であるという指定だ。「car.txt」では、1行目が次のようになっている。
name,maker,color,year:INT,price:INT
これは、データベース用語で言えば項目名(フィールド名)にあたる。要するに、2行目以降にどんな種類のデータがあるかを示しているのだ。car.txtには、このヘッダー行があるので、VALUE属性を「true」にしている。ヘッダー行がなく、1行目からすぐにデータが書かれていれば、ここは「false」を指定する。
パラメータは、この他にもまだあるが、ここではこの程度に抑えておこう。
●DATASRCとDATAFLD
次に、データを表示する部分を見てみよう。次の記述に注目してほしい。
SPAN ID="span1" DATASRC="#carData" DATAFLD="name"></SPAN>
おなじみの<SPAN>タグに、DATASRCとDATAFLDという見慣れない属性が指定してある。DATASRCがデータソースオブジェクトの指定、DATAFLDが項目名の指定だ。
まず、DATASRCには「#carData」と指定してある。「carData」は、先ほど<OBJECT>タグで指定した名前だ。この名前の先頭に#を付けるのがルール。これで、この<SPAN>タグと「carData」という名前のデータソースオブジェクトが連結される。
「DATAFLD」には「name」と指定してある。これは、car.txtのヘッダー行にあった「name」項目のことだ。この指定によって、<SPAN>タグにcar.txtの「name」項目のデータが表示されることになる。
以降の行は、この繰り返しにすぎない。DATASRCに「#carData」と指定し、DATAFLDに「maker」「color」「year」「price」を指定している。これによって、5つのSPAN領域には、次のようなデータが表示されることになる。
span1 …… car.txtのname項目のデータ
span2 …… car.txtのmaker項目のデータ
span3 …… car.txtのcolor項目のデータ
span4 …… car.txtのyear項目のデータ
span5 …… car.txtのprice項目のデータ
●用語解説を兼ねたまとめ
以上のサンプルは、データバインドの例としては、あまりにシンプルすぎて素っ気ない。が、それだけに、これから紹介する内容を理解するうえで重要なポイントが、わかりやすいカタチで含まれている。ここでは、そのポイントをデータバインドで使われる用語解説のカタチで紹介しよう。この時点で解説するには、ちょっと早い内容も含まれているので、ある程度読み進んだら、再び立ち戻って用語を確認してみてほしい。
◆データソース
先の例で言えばcar.txt、つまりWebページ中に表示される元データのことだ。カンマ区切りのテキスト形式が一般的だが、カンマ以外の区切り文字も使用できる(後述)。
◆データソースオブジェクト
Data Source Object。略して「DSO」とも記述される。先のサンプルで言えば、<OBJECT>~</OBJECT>の記述によってWebページ中に生成されるデータソースのオブジェクトだ。といっても、「オブジェクト」か分かりにくい概念なので、ボクなりの解釈で書くと、"データソースの入れ物"がデータソースオブジェクトだ。先のサンプルを例にしよう。car.txtがあっても、そのままだとHTMLファイルで扱えない。そこで、HTMLファイル中にデータソースオブジェクトという"入れ物"を作るのである。そして、そこにcar.txtというデータソースを入れる。こんなイメージだ。
◆データコンシューマ
データソースのデータを表示するHTML要素のことだ。<SPAN><DIV><MARQUEE>などのタグがデータコンシューマにあたる。データベースオブジェクト(入れ物)があって、そこにデータソース(中身)が指定されていれば、入れ物を通じて、中身を自由に取り出して表示できる。その表示する場所が<SPAN><DIV><MARQUEE>などのデータコンシューマというわけだ。コンシューマ(CONSUMER)は「消費者」の意味だから、次のようにたとえられるかもしれない。
データソース……商品
データベースオブジェクト……店
データコンシューマ……消費者
「データソース」という商品がある。この商品を扱う「データソースオブジェクト」という店がある。そして、消費者である「データコンシューマ」が、その店に商品を要求する。すると、消費者のもとに店から商品が届けられる。こんな感じだろうか。
◆データバインド(データ連結)
データソースとデータコンシューマを結びつけることを言う。あるいは、データソースとデータコンシューマを結び付ける仕組み全体と言ってもいい。この記事で解説しているのも、その仕組みであるデータバインドだ。
COLUMN Tabular Data Controlとは何か
じつは、データソースオブジェクト(DSO)は、ActiveXコントロールの1つで、Cなどのプログラミング言語で作成できる(らしい)。資料を読んだかぎりでは、データソースオブジェクトの仕様書みたいなものがあり、その仕様書に沿えば、オリジナルのデータソースオブジェクトを作成できるらしいのである。そして、IE4に最初から組み込まれているデータソースオブジェクトがTabular Data Control(TDC)なのだ。「データベースオブジェクト」が少し抽象的な概念、TDCがその具体的なActiveXコントロールというプログラム、ととらえればいいだろうか(「DSO」が「ワープロ」、「TDC」が「Word」や「一太郎」といった関係か?)。
先にデータソースオブジェクトを"データソースの入れ物"にたとえたが、このたとえに沿えば、IE4に備え付けの"データソースの入れ物"がTDCということになる。
あとで紹介するが、TDCにはいくつかのプロパティやメソッドが用意されていて、これらをスクリプトで操作することで、レコードの絞込や移動ができる。オリジナルのデータソースオブジェクトを作成できるということは、独自のプロパティやメソッドを持つデーソースオブジェクトを作成できるということだから、データの処理方法をプログラマが自由に設計できるということなのだろう。
ただし、それができるのは、もちろんプログラマに限られるだろうから、その能力のないボクのようなユーザーは、「IE4に備え付けのデータベースオブジェクトがTDC」という知識で十分かと思う。
■表示するレコードを切り替える
データバインドの基本と用語を押さえたうえで、次に進もう。今度は少し実用的なサンプルだ。
【サンプル2:smp002.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
P ALIGN=center>
SPAN ID="span1" DATASRC="#carData" DATAFLD="name"></SPAN><BR>
SPAN ID="span2" DATASRC="#carData" DATAFLD="maker"></SPAN><BR>
SPAN ID="span3" DATASRC="#carData" DATAFLD="color"></SPAN><BR>
SPAN ID="span4" DATASRC="#carData" DATAFLD="year"></SPAN><BR>
SPAN ID="span5" DATASRC="#carData" DATAFLD="price"></SPAN><BR>
BUTTON onclick="carData.recordset.MoveFirst()">|<-</BUTTON>
BUTTON onclick="carData.recordset.MovePrevious()"><-</BUTTON>
BUTTON onclick="carData.recordset.MoveNext()">-></BUTTON>
BUTTON onclick="carData.recordset.MoveLast()">->|</BUTTON>
/P>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
もちろん、データソースはcar.txtのままにしてある。画面を見れば一目瞭然だと思うが、要するにレコードを切り替えるボタンを追加している。
先のサンプル1では、データソースであるcar.txtの先頭レコードだけが表示された。それに対し、サンプル2では、ボタンのクリックで表示するレコードを切り替えることができる。これを実現しているのが、4つのボタンのclickイベントで実行される4つのスクリプトだ。ボタンの記述は次のとおりだ。
BUTTON onclick="carData.recordset.MoveFirst()">|<-</BUTTON>
BUTTON onclick="carData.recordset.MovePrevious()"><-</BUTTON>
BUTTON onclick="carData.recordset.MoveNext()">-></BUTTON>
BUTTON onclick="carData.recordset.MoveLast()">->|</BUTTON>
clickイベントやスクリプトの書き方、<、>の記述については割愛するので、分からない方は調べてほしい。さて、スクリプト部分だけを抜き出そう。それぞれ、4つのボタンをクリックすると実行されるスクリプトだ。
carData.recordset.MoveFirst()
carData.recordset.MovePrevious()
carData.recordset.MoveNext()
carData.recordset.MoveLast()
「carData」はデータソースオブジェクトのIDに指定した名前だ。データソースオブジェクトはrecordsetというオブジェクトを持っていて、このrecordsetを介して移動用のメソッドを利用できる。
JavaScriptを知っている方なら、window.document.write() といった記述は見慣れているだろう。windowオブジェクトがdocumentオブジェクトを持っていて、documentオブジェクトにwrite()メソッドが用意されていて……となるわけだが、これと同じととらえていい。「recordsetというオブジェクトはどんなオブジェクトなんだ?」という疑問もあると思うが、まぁ、そういう仕様になっているので、深く考えてもしょうがない。
さて、recordsetオブジェクトのメソッドに目を移そう。各メソッドの機能は次のとおりだ。
MoveFirst()……先頭レコードをカレントレコードにする。
MovePrevious()……1つ前のレコードをカレントレコードにする。。
MoveNext()……1つ先のレコードをカレントレコードにする。
MoveLast()……末尾レコードをカレントレコードにする。
メソッド名を見れば、その機能は自ずと分かるだろう。注意したいのは、「カレントレコード」だ。データソースオブジェクトでは、現在選択されているレコードのことを「カレントレコード(現在レコード)」と呼ぶ。上記の4つのメソッドは、カレントレコードを変更することによって、表示するレコードを切り替えているのである。サンプル1では先頭のレコードだけが表示されたが、初期状態では先頭のレコードがカレントレコードになるからだ。カレントレコードの位置は、次のスクリプトで調べることができる。
carData.recordset.AbsolutePosition
AbsolutePositionはrecordsetオブジェクトのプロパティで、カレントレコードを示す整数値だ。AbsolutePositionは1から始まる。
さて、サンプル2によってレコードを切り替えることができるようになったが、じつはこのサンプル2では、ボタンをクリックして先頭レコードより前、あるいは末尾レコードより後に切り替えようとするとエラーが起きる。これを防ぐには、AbsolutePositionプロパティを使って、サンプル3のように修正しなければならない。
【サンプル3:smp003.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function goFirst() {carData.recordset.MoveFirst()}
function goPrevious() {
if(carData.recordset.AbsolutePosition != 1) { carData.recordset.MovePrevious() }
}
function goNext() {
if(carData.recordset.AbsolutePosition != carData.recordset.RecordCount) { carData.recordset.MoveNext() }
}
function goLast() {
carData.recordset.MoveLast()
}
/SCRIPT>
/HEAD>
BODY>
P ALIGN=center>
SPAN DATASRC="#carData" DATAFLD="name"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="maker"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="color"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="year"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="price"></SPAN><BR>
BUTTON onclick="goFirst()">|<-</BUTTON>
BUTTON onclick="goPrevious()"><-</BUTTON>
BUTTON onclick="goNext()">-></BUTTON>
BUTTON onclick="goLast()">->|</BUTTON>
/P>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
条件分岐によってスクリプトが若干長くなるので、ヘッダ部に関数として定義しているが、やっていることは先のサンプル2と同じだ。ポイントになるスクリプトは次の部分だ。
function goPrevious() {
if(carData.recordset.AbsolutePosition != 1) { carData.recordset.MovePrevious() }
}
function goNext() {
if(carData.recordset.AbsolutePosition != carData.recordset.RecordCount) { carData.recordset.MoveNext() }
}
goPrevious()は1つ前のレコードに移動する関数だが、AbsolutePositionが1でないときだけMovePrevious()メソッドを実行する。AbsolutePositionが1のとき、つまりカレントレコードが先頭レコードであるときは、MovePrevious()メソッドは実行されない。
goNext()は1つ先のレコードに移動する関数だ。この関数では、条件分岐の部分にRecordCountというプロパティを使っている。RecordCountは、レコードの総数を示すrecordsetオブジェクトのプロパティだ。レコードの総数は、次のスクリプトで調べることができる。
carData.recordset.RecordCount
AbsolutePositionプロパティとRecordCountプロパティが一致するのは、カレントレコードで末尾レコードであるときだから、そのときはMoveNext()メソッドを実行しない。
以上で、サンプル2で起きたエラーは防ぐことができる。サンプル2のエラーを防ぐ方法は他にもある。それが、次のサンプル4だ。
【サンプル4:smp004.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function goFirst() {carData.recordset.MoveFirst()}
function goPrevious() {
carData.recordset.MovePrevious() if(carData.recordset.BOF == true) { carData.recordset.MoveFirst() }
}
function goNext() {
carData.recordset.MoveNext() if(carData.recordset.EOF == true) { carData.recordset.MoveLast() }
}
function goLast() {
carData.recordset.MoveLast()
}
/SCRIPT>
/HEAD>
BODY>
P ALIGN=center>
SPAN DATASRC="#carData" DATAFLD="name"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="maker"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="color"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="year"></SPAN><BR>
SPAN DATASRC="#carData" DATAFLD="price"></SPAN><BR>
BUTTON onclick="goFirst()">|<-</BUTTON>
BUTTON onclick="goPrevious()"><-</BUTTON>
BUTTON onclick="goNext()">-></BUTTON>
BUTTON onclick="goLast()">->|</BUTTON>
/P>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
PARAM NAME="TextQualifier" VALUE="">
/OBJECT>
/BODY>
/HTML>
先のサンプル3と異なるのは、goPrevious()とgoNext()の2つの関数だけだ。各関数の記述を抜き出してみよう。
function goPrevious() {
carData.recordset.MovePrevious() if(carData.recordset.BOF == true) { carData.recordset.MoveFirst() }
}
function goNext() {
carData.recordset.MoveNext() if(carData.recordset.EOF == true) { carData.recordset.MoveLast() }
}
各関数では、条件分岐にrecordsetオブジェクトのBOFプロパティとEOFプロパティが使われている。どちらのプロパティも値はtrueかfalseのいずれかをとる。BOFプロパティは、カレントレコードが先頭レコードより前に移動したときtrueとなり、それ以外のときはfalseになる。EOFプロパティは、カレントレコードが末尾レコードより後に移動したときtrueになり、それ以外のときはfalseになる。BOFはBegin Of File、EOFはEnd Of Fileの略だ(たぶん)。
goPrevious()関数では、MoveNext()メソッドを実行したあと、BOFプロパティがtrueかどうか調べる。もしも先頭レコードがカレントレコードのときMoveNext()メソッドを実行すると、カレントレコードが先頭レコードの前になり、BOFプロパティがtrueになる。そしてそのときはMoveFirst()メソッドを実行して、カレントレコードを先頭レコードに戻す。goNext()関数も、考え方は同じだ。
以上、レコードを切り替える際のエラー防止手段を2パターン紹介したが、どちらを使っても問題はない。多くのプロパティを紹介したかったので、あえて2つ紹介したにすぎない。
■recordsetオブジェクトのプロパティとメソッドのまとめ
ここまでにいくつかのプロパティ、メソッドが登場したので、まとめておこう。いずれも、recordsetオブジェクトのプロパティ、メソッドだ。
◆プロパティ
AbsolutePosition……カレントレコードの位置を示す整数値。1から始まる。
RecordCount……レコードの総数
BOF……カレントレコードが先頭レコードより前にあるかどうかを表す。あればtrue、なければfalseになる。
EOF……カレントレコードが末尾レコードより後にあるかどうかを表す。あればtrue、なければfalseになる。
◆メソッド
MoveFirst()……先頭レコードをカレントレコードにする。
MovePrevious()……カレントレコードを1つ戻す。
MoveNext()……カレントレコードを1つ進める。
MoveLast()……末尾レコードをカレントレコードにする。
PART2 繰り返しテーブル
■繰り返しテーブルを作る
ここまでに紹介したのは、データソースであるcar.txtのカレントレコードを表示することだ。つまり、Webページに表示されるレコードは1つだけ。それに対し、これから紹介する方法を使えば、すべてのレコードを表示できる。さっそくサンプルを紹介しよう。なお、データソースはcar.txtのままだ。
【サンプル5:smp005.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
TABLE DATASRC="#carData" BORDER=1>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
これをIE4に読み込むと、すべてのレコードが表形式で表示されているのが確認できるだろう。これが、「繰り返しテーブル」だ。テーブルの記述を見てみよう。
TABLE DATASRC="#carData" BORDER=1>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TABLE>
1つの<TR>~</TR>のあいだに<TD>~</TD>が5つある。つまり、HTMLの常識からすると、このテーブルは1行×5列にしかならないはずだ。が、実際には、1行ではなく、car.txtに記述されているレコード数分の行数になっている。記述上は1行×5列なのに、それがレコードの数だけ繰り返して表示されているのである。
TABLE>タグの属性に「DATASRC="#carData"」と指定してある。これによって、このテーブルでデータソースオブジェクト(IDがcarData)を扱うことを宣言する。そして、表示する項目名を<TD>~<TD>のあいだに<SPAN>タグを使って記述する。たとえばname項目のデータを表示するなら、<TD>~</TD>のあいだに次のように書く。
SPAN DATAFLD="name"></SPAN>
これによって、name項目のデータがレコードの数だけ繰り返し表示されることになる。他の項目も同様だ。
いかがだろうか。データソースのテキストファイルを用意しておけば、こんなに手軽に大きな表を作成できるのだ。しかも、この表はけっして静的ではない。並び替えや絞込を行って、表示するレコードをダイナミックに変化させることもできる。詳しくは後述するが、繰り返しテーブルは、表作成を簡素化すると同時に、ダイナミックな表を作る強力な機能なのだ。
■繰り返しテーブルに見出しを付ける
先ほどの繰り返しテーブルには、見出しがない。「車種」「メーカー」……などの見出し行があれば、もっと見やすいはずだ。ということで、<TABLE>~</TABLE>のあいだに、もう1つ<TR>~</TR>を追加して修正したのが次のサンプルだ。結論から言うと、これではうまくいかない。
【サンプル6:smp006.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
TABLE DATASRC="#carData" BORDER=1>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
実際に見てもらえば分かるが、見出しは表示されているものの、見出しそのものも繰り返し表示されてしまう。エラーにはならないが、これでは、どう見ても煩雑すぎる。見出し行だけ繰り返さない工夫が必要だ。それが、次のサンプルだ。
【サンプル7:smp007.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
TABLE DATASRC="#carData" BORDER=1>
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
THEAD>と<TBODY>がポイントだ。これはHTML4.0から採用されたタグで、<THEAD>で表のヘッダ、<TBODY>で表の本体を独立して記述できる。これなら、見出し行は繰り返されず、本体部分だけが繰り返される。
■一度に表示するレコード数を指定する
レコード数がそれほど多くなければ、繰り返しテーブルで全レコードを表示してもいいだろうが、レコード数が多いと、一度に表示するレコード数を制限し、切り替えられた方が便利だろう。一度に表示するレコード数は、<TABLE>タグにDATAPAGESIZEという属性を指定すれば制限できる。次が、そのサンプルだ。
【サンプル8:smp008.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
TABLE DATASRC="#carData" BORDER=1 DATAPAGESIZE=5>
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
TABLE>タグの属性にDATAPAGESIZE=5と指定しているため、先頭から5レコード分しか表示されない。次に必要なのが、表示するレコードを切り替えることだ。これには、previousPage()とnextPage()というメソッドを使う。サンプルは次のとおり。
【サンプル9:smp009.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
CENTER>
BUTTON onclick="table1.previousPage()">BACK</BUTTON>
BUTTON onclick="table1.nextPage()">NEXT</BUTTON>
TABLE ID="table1" DATASRC="#carData" BORDER=1 DATAPAGESIZE=5>
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
/CENTER>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
まず、<TABLE>タグでID="table1"として、table1を参照できるようにしている。そして、2つのボタンのclickイベントによって実行されるのが、次の2つのスクリプトだ。
table1.previousPage()
table1.nextPage()
「table1」は、もちろんテーブルのIDだ。そして、previousPage()が次ページ、nextPage()が前ページに切り替えるメソッドだ。たとえば、DATAPAGESIZEが「5」で全レコード数が12個だとしよう。この場合、まず1~5のレコードが表示され、nextPage()メソッドを実行すると6~10が表示される。続けてnextPage()メソッドを実行すると、11~12の2件分のレコードが表示される。previousPage()メソッドは、この逆だ。
PART3 データコンシューマ
■データコンシューマいろいろ
ここまでに、<SPAN>タグを使ってカレントレコードを表示して切り替える例、<TABLE>タグによる繰り返しテーブルの例を紹介した。<SPAN>と<TABLE>は、データバインドの用語で言えば「データコンシューマ」である。データコンシューマは、他にもある。たとえば<MARQUEE>がそうだ。ここでは、<MARQUEE>を使ったデータバインドの例を紹介しよう。
まず、データソースとして次のテキストファイルを用意する。ファイル名はmessage0.txtとしておこう。
【message0.txt】
message
Welcome to DynamicHTML World!
このページは井上健語によって制作されています
出身地・・・四国。星座・・・獅子座
そして、次のようなHTMLファイルを用意する。
【サンプル10:smp010.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function goPrevious() {if(msgData.recordset.AbsolutePosition != 1) { msgData.recordset.MovePrevious() }}
function goNext() {if(msgData.recordset.AbsolutePosition != msgData.recordset.RecordCount) { msgData.recordset.MoveNext() }}
/SCRIPT>
/HEAD>
BODY>
BUTTON onclick="goPrevious()">BACK</BUTTON>
BUTTON onclick="goNext()">NEXT</BUTTON>
MARQUEE DATASRC="#msgData" DATAFLD="message" DATAFORMATAS="text"></MARQUEE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=msgData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="message0.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
ページ中の「BACK」と「NEXT」のボタンをクリックすると、マーキー表示のメッセージが切り替わる。メッセージは、先のmessage0.txtに書いてあった文字列だ。
復習も兼ねて、両ファイルの記述を少し詳しく見てみよう。まず、テキストファイルでは、先頭行に「message」と書いてある。これは項目名だ。そして、以降の各行には、文字列を1行ごとにリターンで区切って入力してある。
次にHTMLファイルの<OBJECT>タグを見てみよう。IDは「msgData」とし、パラメータの指定が次のようになっている。
PARAM NAME="DataURL" VALUE="message0.txt">
PARAM NAME="UseHeader" VALUE="true">
1つ目のパラメータ指定でVALUEを「message0.txt」を指定し、データソースをmessage0.txtにしてある。message0.txtの先頭行は項目名なのでヘッダーありだ。だから、2つ目のパラメータのVALUEは「true」にしてある。
ヘッダー部で定義しているのは、レコードを切り替える2つの関数だ。MovePrevious()とMoveNext()の2つのメソッドを使って、以前に解説したエラーにならない処理をしてある。
以上は、これまでに紹介した内容だから、分かりにくい点はないだろう。若干新しい内容が含まれているのは、次の記述だ。
MARQUEE DATASRC="#msgData" DATAFLD="message" DATAFORMATAS="text"></MARQUEE>
DATASRCとDATAFLDについては、すでに紹介したとおりだ。DATASRCがデータソースオブジェクトのIDの指定、DATAFLDが表示する項目の指定だ。目新しいのは、これらが<MARQUEE>タグの属性として指定されていることとDATAFORMATASだ。まず、<MARQUEE>の属性にDATASRCとDATAFLDを指定できるのは、<MARQUEE>がデータコンシューマだから不自然ではない。要するに、<SPAN>や<TABLE>のように、<MARQUEE>もデータバインドで使えるタグ、ということだ。
DATAFORMATASは、DATASRCやDATAFLDと同様にデータバインド用の属性で、データソースのデータ形式を示している。このサンプルでは、DATAFORMATAS="text"として、データソース(message0.txt)のデータ形式がテキストであることを指定してある。これを、DATAFORMATAS="html"とすると、データソース中のデータがHTMの記述Lとして解釈されるようになる。
具体的な例で紹介しよう。まず、データソースのテキストファイルとして、次のようなファイルを用意する。ファイル名はmessage1.txtとしよう。
【message1.txt】
message
SPAN STYLE="color:red;font-size:20pt;font-family:Arial;">Welcome to DynamicHTML World!</SPAN>
I>このページは井上健語によって制作されています</I>
TABLE BORDER=1><TR><TD>出身地</TD><TD>四国</TD></TR><TR><TD>星座</TD><TD>獅子座</TD></TR></TABLE>
先ほどのテキストファイルと異なるのは、HTMLの記述が含まれていることだ。次に、HTMLファイルを次のように修正する。
【サンプル11:smp011.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function goPrevious() {if(msgData.recordset.AbsolutePosition != 1) { msgData.recordset.MovePrevious() }}
function goNext() {if(msgData.recordset.AbsolutePosition != msgData.recordset.RecordCount) { msgData.recordset.MoveNext() }}
/SCRIPT>
/HEAD>
BODY>
BUTTON onclick="goPrevious()">BACK</BUTTON>
BUTTON onclick="goNext()">NEXT</BUTTON>
MARQUEE DATASRC="#msgData" DATAFLD="message" DATAFORMATAS="html"></MARQUEE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=msgData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="message1.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
先ほどのHTMLファイルと異なるのは2カ所だけだ。まず、<MARQUEE>タグのDATAFORMATASの指定が「text」ではなく「html」になっている。そして、データソースをmessage1.txtにしてある。これを読み込めば、マーキー表示の内容が、テキストファイル中のHTMLの記述にしたがっているのが分かるだろう。もしも、DATAFORMATASを「text」にしてあると、HTMLのタグがたんなる文字列として解釈され、そのまま表示されることになる。
なお、DATAFORMATASの初期値は「text」だ。このため、先ほどのサンプル10では、DATAFORMATASを省略しても問題はない。テキストファイル中にHTMLのタグを含め、それをHTMLのタグとして解釈させたいときに、DATAFORMATAS="html"と指定するのが一般的だろう。
■データバインド拡張機能とデータコンシューマ
これまでに、DATASRCやDATAFORMATASなどの属性を使ってきたが、これらはマイクロソフトがデータバインドを実現するためにHTMLを独自に拡張した機能だ。具体的には、次の4つが拡張された属性だ。
DATASRC
DATAFLD
DATAFORMATAS
DATAPAGESIZE
そして、データコンシューマであるタグでこうした属性を指定することによって、データバインドを実現しているのである。
データコンシューマとして利用できるタグは、別表のとおりだ。これらのタグでは、必ずしも4つの属性すべてを指定できるわけではない。ここまでは、このうち、<SPAN><TABLE><MARQUEE>の各タグを使ったデータバインドの例を紹介したわけだが、表を見れば、データバインドの世界はまだまだ奥が深いことが想像できるだろう。なお、筆者は<APPLET>タグや<INPUT>タグを使ったデータバインドで何か可能になるかは確認していない。
PART4 並び替えと絞込
いよいよ、データバインドの真骨頂と言うべき、レコードの並び替えと絞込を紹介しよう。並び替えも絞込も、カレントレコードだけを表示しても意味はないから、必然的に繰り返しテーブルを使うことになる。DATAPAGESIZEで一度に表示するレコード数を制限することもできるが、処理がややこしくなるので、ここではすべてのレコードを表示した繰り返しテーブルを前提にしよう。
■レコードの並び替え
まず、並び替えからだ。次は、これまでに出てきた中古車データの繰り返しテーブルを、価格の昇順/降順で並び替えるサンプルだ。
【サンプル12:smp012.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
BUTTON onclick="carData.Sort='+price';carData.Reset();">価格昇順</BUTTON>
BUTTON onclick="carData.Sort='-price';carData.Reset();">価格降順</BUTTON>
BUTTON onclick="carData.Sort='';carData.Reset();">RESET</BUTTON>
TABLE DATASRC="#carData" BORDER=1 STYLE="font-size:9pt;">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
価格昇順>ボタンなら価格の安い方から高い方に並び替えられ、<価格降順>ならその逆になる。<RESET>ボタンをクリックすれば、初期状態、つまり並び替える前の状態に戻る。各ボタンで実行しているスクリプトを次に抜き出してみよう。いずれも、とてもシンプルだ(ソース中ではonclick=に続けて1行で書いてあるので、以下では複数行に分けている)。
価格昇順>ボタン
carData.Sort="+price"
carData.Reset()
価格降順>ボタン
carData.Sort="-price"
carData.Reset()
RESET>ボタン
carData.Sort=""
carData.Reset()
carDataがデータソースオブジェクト、Sortがそのプロパティだ。そして、Sortプロパティには"+price"や"-price"を値として設定している。"+price"ならprice項目の昇順、"-price"ならprice項目の降順という条件が設定される。実際に並び替えるには、最後のReset()メソッドが必要だ。Reset()メソッドを実行することで、Sortプロパティに指定した条件でレコードが並び替えられる。
RESET>ボタンで実行されるスクリプトでは、Sortプロパティに空文字("")を指定してReset()メソッドを実行している。Sortプロパティが空になるので、並び替える以前の状態に戻る。
COLUMN データソースのデータ型の指定
昇順/降順によってどう並び替えられるかは、データの種類(文字列、数値、日付など)によって異なる。このため、各項目のデータの種類(データ型)をあらかじめ指定しておくことが重要になる。このサンプルであれば、データソースであるcar.txtの先頭行(ヘッダー行)がそれだ。car.txtのヘッダー行は次のようになっている。
name,maker,color,year:Int,price:Int
year:Int,price:Int の部分がデータ型の指定だ。Intは整数型の指定なので、year項目とprice項目のデータは整数値として扱われることになる。指定方法は「項目名:データ型」とする。データ型を指定しなければ、自動的に文字型になる。このため、name項目、maker項目、color項目のデータは文字列として扱われる。
データ型を正しく指定しないと、並び替えや絞込ができないので注意したい。たとえば、car.txtのヘッダー行を次のように指定したとしよう。
name,maker,color,year,price
この場合、すべての項目のデータは文字列として扱われることになる。この状態でprice項目、つまり価格によってレコードを並び替えても、数値が文字列として扱われるため、数値の昇順/降順では並び替えられない。あるいは、100万円以上という条件で絞り込もうとしても、「800000」や1500000」が文字列として扱われるため、条件そのものが成立せず、絞込はできなくなる。注意したいポイントだ。ヘッダー行で指定できるデータ型は次のとおりだ。
String……文字列(初期設定)
Date……日付データ
Boolen……真偽値
Int……整数値
Float……浮動小数値
先のサンプルは、価格の昇順/降順でレコードを並び替えるだけだった。並び替えの仕組みを知るうえでは、シンプルで分かりやすいと思うが、どうせなら、すべての項目について昇順/降順で並び替えられた方が便利だろう。次がそのサンプルだ。スクリプトに少し工夫を加えただけで、並び替えの方法そのものは、すでに紹介した以上のことはしていない。
【サンプル13:smp013.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function sortRecord() {var condition = form1.sort_order.value + form1.sort_field.value carData.Sort = condition carData.Reset()}
/SCRIPT>
/HEAD>
BODY>
FORM ID="form1">
SELECT ID="sort_field" onchange="sortRecord()">
OPTION SELECTED VALUE="name">車名
OPTION VALUE="maker">メーカー
OPTION VALUE="color">色
OPTION VALUE="year">生産年
OPTION VALUE="price">価格
/SELECT>
SELECT ID="sort_order" onchange="sortRecord()">
OPTION SELECTED VALUE="+">昇順
OPTION VALUE="-">降順
/SELECT>
INPUT TYPE="button" VALUE="RESET" onclick="carData.Sort='';carData.Reset()">
/FORM>
TABLE DATASRC="#carData" BORDER=1 STYLE="font-size:9pt;">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
/DIV>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
左のリストで項目を選び、右のリストで昇順/降順を指定すれば、即座にレコードが並び替えられる。<RESET>ボタンは、先のサンプルと同じだ。
リストには<SELECT>タグを使っている。<SELECT>タグについては省略するので、分からない方はHTMLの書籍等で調べてほしい。ポイントになる記述だけを簡略化して抜き出すと、次のようになる。
FORM ID="form1">
SELECT ID="sort_field" onchange="sortRecord()">
:/SELECT>
SELECT ID="sort_order" onchange="sortRecord()">
:/SELECT>
/FORM>
まず、<FORM>のIDを「form1」とし、その内側にある2つの<SELECT>のIDを「sort_field」、「sort_order」としている。そして、2つの<SELECT>タグでは、onchange="sortRecord()" として、選択された値が変わったときにsortRecord()関数を実行するようにしてある。IDの指定は、このsortRecord()関数で重要な役割を演じる。sortRecord()関数の定義は次のとおりだ。
function sortRecord() {
var condition = form1.sort_order.value + form1.sort_field.value carData.Sort = condition carData.Reset()
}
まず、ローカル変数conditionに、form1.sort_order.valueとform1.sort_field.valueを結合した値を入れる。「form1」「sort_order」「sort_field」は、先に指定したIDだ。このように、「<FORM>のID.<SELECT>のID.value」と記述することで、そのSELECT要素で選択されている値(value)を参照できる。だから、form1.sort_order.valueは"+"か"-"の文字列になる。そして、form1.sort_field.valueは"name"、"maker"、"color"、"year"、"price"のいずれかの文字列になる。この2つの文字列を結合するから、ローカル変数conditionには、"+color"、"-price"、"+color"といった文字列が入ることになるわけだ。
ここまで分かれば、あとは簡単だろう。carData.Sort = condition でSortプロパティに並び替えの条件を設定し、carData.Reset()でその条件で並び替えを行っているだけだ。
並び替えの仕組みが分かれば、あとはいろいろ応用ができると思う。このサンプルは、その一例にすぎない。項目名の文字列をクリックするごとに、その項目のデータを基準にレコードを昇順/降順に並び替えたり、ラジオボタンを使ったり……等々。並び替えの仕組みそのものは非常にシンプルだから、あとはスクリプトの力量とアイデア次第というところだ。
■レコードの絞込
絞込とは、特定の条件に合うレコードだけを表示し、条件に合わないレコードを消してしまうことだ。先の中古車データなら、「白い車」とか「100万円以下の車」、「白い車で100万円以下の車」といった条件に合うレコードだけを表示できるわけだ。次が、そのサンプルだ。
【サンプル14:smp014.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
BUTTON onclick="carData.object.Filter='color=白';carData.Reset();">白い車</BUTTON>
BUTTON onclick="carData.object.Filter='price<=1000000';carData.Reset();">100万以下の車</BUTTON>
BUTTON onclick="carData.object.Filter='color=白&price<=1000000';carData.Reset();">白で100万以下の車</BUTTON>
BUTTON onclick="carData.object.Filter='';carData.Reset();">RESET</BUTTON>
TABLE DATASRC="#carData" BORDER=1 STYLE="font-size:9pt;">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR>
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
/DIV>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
PARAM NAME="TextQualifier" VALUE="">
/OBJECT>
/BODY>
/HTML>
ページ中のボタンに表示される文字のとおり、<白い車>ボタンをクリックすれば「色」が「白」のレコードだけに絞り込まれ、<100万以下の車>ボタンなら、「価格」が「1000000」以下のレコードだけが絞り込まれ、<白い車で100万以下の車>ボタンなら、「色」が「白」かつ「価格」が「1000000」以下のレコードだけが絞り込まれて表示される。<RESET>ボタンなら、絞り込まれる前の状態に戻る(すべてのレコードが表示される)。
4つのボタンには、onclickに続けて、直接スクリプトを書いている。4つのボタンのクリックで実行されるスクリプトを見てみよう。
白い車>ボタン
carData.object.Filter="color=白"
carData.Reset()
100万以下の車>ボタン
carData.object.Filter="price<=1000000"
carData.Reset()
白で100万以下の車>ボタン
carData.object.Filter="color=白&price<=1000000"
carData.Reset()
RESET>ボタン
carData.object.Filter=""
carData.Reset()
いずれも、carData.object.Filter=に続けて絞込の条件を指定してある。carDataがデータソースオブジェクトのIDで、以下、「.object.Filter=」として右辺に条件を記述する。「object.Filter=」の部分は固定と考えていい。そして、並び替えと同様にReset()メソッドを実行する。
条件は、「項目名=データ」のように、項目名とデータを「=」などの記号で結んで式にする。「=」の部分に使える記号は次のとおりだ。
= …… 等しい > …… より大きい >= …… 以上 < …… より小さい <= …… 以下 <> …… 等しくない
条件にはワイルドカードの*も使える。たとえば「maker=*ダ」とすれば、maker項目のデータが「ダ」で終わるレコードが絞り込まれる。この例なら、「ホンダ」と「マツダ」のレコードが絞り込まれることになる。「maker=ダ*」なら「ダ」で始まるデータ、「maker=*ダ*」なら「ダ」を含むデータとなる。
複数の条件をANDやORで結ぶこともできる。ANDなら&、ORなら|を使う。例を示しておこう。
color=白&price<=1000000……色が白で100万円以下の車
maker=ホンダ|maker=マツダ……メーカーがホンダかマツダの車
(maker=ホンダ|maker=マツダ)&year>94……メーカーがホンダかマツダで94年以降に生産された車
絞込の方法は、以上だ。思った以上に簡単なのが分かったと思う。
PART5 補足情報
■Tabular Data Controlのプロパティ
TDC(Tabular Data Control)が、IE4に最初から用意されているデータソースオブジェクトであることは、先に紹介した。TDCをWebページで扱えるようにするには、次のように記述すればいい。これまでのサンプルにも、すべてこの記述が含まれていた。
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
PARAM NAME="TextQualifier" VALUE="">
/OBJECT>
ここで、<PARAM>タグに注目しよう。これは、TDCに用意されているプロパティの指定だ。たとえば、<PARAM NAME="DataURL" VALUE="car.txt">なら、NAME="DataURL"がプロパティ名、VALUE="car.txt"がそのプロパティの値となる。ここでは、TDCのプロパティをまとめて紹介しておこう。なお、けっこう細かいので、すべてを理解する必要はないと思う。参考程度にとどめておけばいいだろう(ボクもよく分かってない)。
AppendData
新しいデータソースを指定したとき、そのデータを現在のデータに追加するか、置き換えるかの指定。値はtrueかfalse。trueなら追加され、falseなら置き換わる。初期設定はfalse。試してないので、実際の動作は未確認。
指定例:<PARAM NAME="AppendData" VALUE="true">
CaseSensitive
大文字と小文字を区別するかどうかの指定。値はtrueかfalse。trueなら区別され、falseなら区別されない。初期設定はtrue。試してないので、実際の動作は未確認。
指定例:<PARAM NAME="CaseSensitive" VALUE="false">
CharSet
データソースのテキストファイルの文字セットの指定。試してないので、実際の動作は未確認。
指定例:<PARAM NAME="CharSet" VALUE="iso-2022-jp">
DataURL
データソースのテキストファイルの指定。
指定例:<PARAM NAME="DataURL" VALUE="car.txt">
EscapeChar
エスケープキャラクターの指定。FieldDelim、RowDelim、TextQualifierで指定した文字そのものをデータとして扱いたいとき、その直前に入れる文字。たとえば、データソース中に次のようなレコードがあるとする。
サニー,ニッサン,シルバー,96,1200000
このうち、「1200000」に3桁ごとのカンマを入れて「1,200,000」とすると、初期設定では「1」「200」「000」の3つの文字列として扱われてしまう。この場合、EscapeCharにたとえば「\」を指定しておくと、次のように記述することで、カンマをデータの一部として扱うことが可能になる(この場合、「1200000」のデータ型は文字列とする)。
サニー,ニッサン,シルバー,96,1\,200\,000
初期設定ではEscapeCharは指定されていない。
指定例:<PARAM NAME="EscapeChar" VALUE="\">
FieldDelim
項目データの区切りを示す記号の指定。初期設定は「,」。以下は、FieldDelimにタブを指定する例。
指定例:<PARAM NAME="FieldDelim" VALUE=" ">
Filter
レコードを絞り込んで表示する条件。初期設定は空文字。
指定例:<PARAM NAME="Filter" VALUE="price<1000000">
Language
データソースのテキストファイルの言語の指定。試してないので、実際の動作は未確認。
RowDelim
レコードの区切りを示す記号の指定。初期設定は改行コード。
Sort
レコードを並び替えて表示する条件。初期設定は空文字。
指定例:<PARAM NAME="Sort" VALUE="+price">
TextQualifier
文字列データであることを明示的に示す記号の指定。初期設定はダブルクォート(")。データソース中で、たとえば次のように指定しておけば、「1,200,000」の「,」は文字列の一部として扱われる。
サニー,ニッサン,シルバー,96,"1,200,000"
指定例:<PARAM NAME="TextQualifier" VALUE="'">
UseHeader
データソースの1行目をヘッダー行とするかどうかの指定。値はtrueかfalseのいずれか。trueならヘッダー行として扱い、falseならデータ行として扱う。
指定例:<PARAM NAME="UseHeader" VALUE="false">
■ヘッダー行がない場合の処理
ここまでのサンプルでは、データソースであるテキストファイルの先頭がヘッダー行であることを前提にしていた。原則として、項目名とデータ型を指定できるヘッダー行は、あるにこしたことはない。しかし、場合によってはヘッダー行のないデータを扱う必要に迫られるかもしれない。以下は、そうしたケースでの処理方法だ。「ヘッダー行は必ず付ける」ということなら、読み飛ばしていただいてけっこうだ。
ヘッダー行のないテキストファイルでは、1行目からいきなりデータが書かれている。たとえば、次のようになっているわけだ。このファイルをcar_nohead.txtとしよう。
【car_nohead.txt】
アコード,ホンダ,白,92,800000
マークⅡ,トヨタ,白,94,1500000
カムリ,トヨタ,黒,90,550000
: :
この場合、カレントレコードを表示したり、全レコードを繰り返しテーブルで表示するにはどうしたらいいだろう。これまで紹介したように、いずれの場合もDATASRCとDATAFLDを指定する必要があるが、項目名がないためdataFldが指定できない。
答えは簡単。column1、column2、column3……という文字列を使うのだ。ただし、データ型の指定がないため、すべてのデータは初期設定の文字列として扱われる。したがって、並び替えや絞込では大きな制限を受けざるをえない。この意味でも、ヘッダー行はできるだけ付けたほうがいい。
次のサンプルは、ヘッダー行のないcar_nohead.txtをデータソースとする、繰り返しテーブルの例だ。
【サンプル15:smp015.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
BUTTON onclick="carData.object.Filter='column3=シルバー';carData.Reset();">シルバーの車</BUTTON>
BUTTON onclick="carData.Sort='-column5';carData.Reset();">価格降順</BUTTON>
BUTTON onclick="carData.Sort=;carData.object.Filter=;carData.Reset();">RESET</BUTTON>
TABLE DATASRC="#carData" BORDER=1 STYLE="font-size:9pt;">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR>
TD><SPAN DATAFLD="column1"></SPAN></TD>
TD><SPAN DATAFLD="column2"></SPAN></TD>
TD><SPAN DATAFLD="column3"></SPAN></TD>
TD><SPAN DATAFLD="column4"></SPAN></TD>
TD><SPAN DATAFLD="column5"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
/DIV>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car_nohead.txt">
PARAM NAME="UseHeader" VALUE="false">
/OBJECT>
/BODY>
/HTML>
3つのボタンで絞込と並び替えも行っているが、価格の数値(column5項目のデータ)も文字列として扱われるため、数値の大小での並び替えはできない。ソースの記述は、これまでの内容が分かっていれば特に難しくないので、ヘッダー行のないデータを扱う際の参考にしてほしい。
PART6 スクリプト作成に役立つ
いくつかの実験
ここまで分かったら、スクリプトを駆使して、バリバリのデータベースライクなWebページを作りたいと思うだろう。実際、スクリプトとスタイルシートが分かっていれば、かなりのことができると思う。ただし、まだ少し知っておきたい知識が残っている。少しばかりストイックすぎる気もするが、それをここで紹介したい。一見すると地味な内容なので斜め読みでかまわないが、スクリプトを書いていて何か分からないことが起きたら、もう一度目を通してみよう。解決の糸口が見つかるかもしれない。なお、いちばん最初のrecordNumberプロパティだけは理解しておきたい。スクリプトを書くうえで強力な武器になるはずだ。
■recordNumberプロパティは繰り返しテーブルの表示順
まず、これまでに紹介しなかったrecordNumberプロパティを取り上げよう。これは、繰り返しテーブルで表示されるレコードの表示順のことで、表示される順番に1、2、3……という整数値をとる。次のサンプルは、繰り返しテーブルで表示されたレコード(行)をクリックすると、そのレコードのrecordNumberプロパティをアラート表示する例だ。
【サンプル16:smp016.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
TABLE ID="table1" DATASRC="#carData" BORDER=1 STYLE="font-size:9pt">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR STYLE="cursor:hand;" onclick="alert(this.recordNumber)">
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
ポイントになっているのは、次の行だ。
TR STYLE="cursor:hand;" onclick="alert(this.recordNumber)">
まず、STYLE="cursor:hand;" はスタイルシートの指定なので、データバインドには無関係なのだが、ついでに解説しておくと、カーソルを手の形にする指定だ。そして、clickイベントで実行されるスクリプトが、"alert(this.recordNumber)"。thisは、<TR>というタグそのもの(というか<TR>というオブジェクト)を指す。JavaScriptに不慣れだとthisの意味は理解しづらいと思うので、分からなければ無理に理解することはない。あえて理解しようとするなら、thisは「これ」「こいつ」と考えていい。つまり、スクリプトの意味は「クリックされたら、こいつのrecordNumberプロパティをアラート表示しろ」ということだ。
そして、ここが重要なのだけれど、この表は繰り返しテーブルだということだ。つまり、記述の上では<TR>~</TR>が1つの1行だけなのに、その<TR>~</TR>の部分が繰り返し表示されて複数行の表になっている。<TR>~</TR>の部分が繰り返されるわけだから、<TR>タグ内に書いた STYLE="cursor:hand;" と onclick="alert(this.recordNumber)" も繰り返されることになる。
その結果、各行(レコード)にマウスカーソルを置くとカーソルが手の形になり、クリックすると「こいつ」、つまり「この行(=レコード)」のrecordNumberプロパティ」がアラート表示されることになるのだ。
「なんだか分からない」と思われても当然のような説明で恐縮なのだが、分からなかったらごめんなさい。ただ、こんなに短いスクリプトで、繰り返しテーブルの各行(レコード)の表示順を取り出せることだけには、注目してほしいと思う。つまり、クリックしたレコードが何番目なのかをスクリプトで知ることができるのだ。得られた情報は、スクリプトで何かしたいとき、とても便利な情報として利用できる。
■並び替え、カレントレコード、recordNumberプロパティの関係
並び替えの方法はすでに紹介した。ここでは、並び替えを行うと、カレントレコードとrecordNumberプロパティがどう変化するかを確認しておきたい。
ここまでにサンプルとした中古車の繰り返しテーブルを思い出してほしい。ページを読み込んだ直後は、データソースであるテキストファイルの順番に各レコードが表示される。そして、そのときカレントレコードは先頭のレコードになっている。
さて、ここで価格の安い順(降順)に並び替えたとしよう。この場合、カレントレコードは、
並び替える前の先頭レコード
並び替えた後の先頭レコード
のどちらになるだろうか? さらに、recordNumberプロパティはどう変化するだろう。読み込んだ直後は、表示される順番に1、2、3……となっているはずだが、並び替えた後は、
並び替える前の順番が受け継がれる
並び替えた後の順番になる
のどちらだろうか? 次のサンプルは、以上の2点を確認するためのものだ。
【サンプル17:smp017.html】
HTML>
HEAD>
TITLE></TITLE>
/HEAD>
BODY>
BUTTON onclick="carData.Sort='+price';carData.Reset()">価格昇順</BUTTON>
BUTTON onclick="carData.Sort='-price';carData.Reset()">価格降順</BUTTON>
BUTTON onclick="carData.Sort='';carData.Reset()">RESET</BUTTON><BR>
SPAN DATASRC="#carData" DATAFLD="name"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="maker"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="color"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="year"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="price"></SPAN>
TABLE ID="table1" DATASRC="#carData" BORDER=1 STYLE="font-size:9pt">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR STYLE="cursor:hand;" onclick="alert(this.recordNumber)">
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
このサンプルでは、カレントレコードを表示すると同時に繰り返しテーブルも表示している。そして、<価格昇順><価格降順><RESET>の3つのボタンを用意し、レコードの並び替えができる。さらに、繰り返しテーブルの各レコード(行)をクリックすると、そのレコードのrecordNumberプロパティがアラート表示される。
ソースは、これまで扱った以上の内容ではないので、解説は省略する。ポイントは、3つのボタンをクリックして並び替えを行うと、並び替えたあとの先頭レコードがカレントレコードになることだ。さらに、レコードをクリックしてrecordNumberプロパティを調べても、並び替えたあとの順番に1、2、3……となる。まとめると、次のとおりだ。
並び替えを行うと、並び替え後の先頭レコードがカレントレコードになる。recordNumberプロパティは表示される順番に1、2、3……となる。
これだけだと、「だからどうした」と思われるだけだろうが、スクリプトを使っていろいろやっていると、必ずこの知識が必要になるときがくる。
■絞込、カレントレコード、recordCountプロパティ、recordNumberプロパティの関係
絞込を行うと、繰り返しテーブルに表示されるレコード数は少なくなる。では、そのときレコードの総数を示すrecordNumberプロパティはどう変化するだろうか。表示されるレコードが少なくなるから、recordNumberプロパティも少なくなるのだろうか。あるいは、もともとのレコード数が維持されるのだろうか。
さらに、絞込を行うと、カレントレコードはどう変化するだろう。たとえば、ページを読み込んだ直後は、先頭にある「ホンダのアコード」のレコードがカレントレコードになるが、「トヨタ」のレコードだけを絞り込んだら、カレントレコードは「ホンダのアコード」のままだろうか、あるいは絞り込んだあとの先頭レコードだろうか。
もう1つ、recordNumberプロパティはどうだろう。繰り返しテーブルで全レコードを表示している状態では、先頭から順番に1、2、3……となるのは分かる。では、絞り込んだ結果、絞込前にrecordNumberプロパティが5、7、10だった3つのレコードだけが表示されたらどうだろうか。各レコードのrecordNumberプロパティは5、7、10のままだろうか。それとも、表示される順番に1、2、3となるだろうか。
次のサンプルが、以上の疑問の答えになっている。
【サンプル18:smp018.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function toyotaFilter() {carData.object.Filter="maker=トヨタ" carData.Reset() checkTotal()}
function mazdaFilter() {carData.object.Filter="maker=マツダ" carData.Reset() checkTotal()}
function resetRecord() {carData.object.Filter="" carData.Reset() checkTotal()}
function checkTotal() {input1.value = carData.recordset.recordCount}
/SCRIPT>
/HEAD>
BODY onload="checkTotal()">
BUTTON onclick="toyotaFilter()">トヨタ車</BUTTON>
BUTTON onclick="mazdaFilter()">マツダ車</BUTTON>
BUTTON onclick="resetRecord()">RESET</BUTTON><BR>
recordCount=<INPUT ID="input1" SIZE=2></INPUT><BR>SPAN DATASRC="#carData" DATAFLD="name"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="maker"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="color"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="year"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="price"></SPAN>
TABLE ID="table1" DATASRC="#carData" BORDER=1 STYLE="font-size:9pt">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR STYLE="cursor:hand;" onclick="alert(this.recordNumber)">
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
3つのボタンの下には、レコード総数を示すrecordCountプロパティの値が表示される。次の行が、その表示箇所の記述だ。
recordCount=<INPUT ID="input1" SIZE=2></INPUT><BR>
そして、ヘッダー部に、recordCountプロパティを表示するcheckTotal()関数を定義している。
function checkTotal() {
input1.value = carData.recordset.recordCount
}
そして、このcheckTotal()を、ページが読み込まれたとき(onload)、3つのボタンのいずれかがクリックされたとき実行している。
読み込んだ直後は、繰り返しテーブルで全レコードが表示されるので数値は19になる。絞込の各ボタンをクリックすれば、この数値が変化するのが確認できるだろう。つまり、recordCountプロパティは、絞り込んだ後に表示されるレコード数ということになる。
recordCountプロパティの下は、カレントレコードの表示だ。絞り込むと、カレントレコードは絞り込んだ後の先頭レコードになるのが確認できるはずだ。
各レコードをクリックすると、そのレコードのrecordNumberプロパティがアラート表示される。絞り込む前も後も、表示される順番に1、2、3……となる。つまり、絞り込んでも、recordNumberプロパティはあくまで表示されているレコードの順番になるということだ。まとめると、次のとおりだ。
絞込を行うと、絞込後の先頭レコードがカレントレコードになる。さらに、recordCountプロパティは絞込後に表示されているレコード数になる。recordNumberプロパティは表示される順番に1、2、3……となる。
これも、「だから、何なんだ」かもしれないが、やはりスクリプトをいろいろと書いていくと、貴重な情報になると思う。
■nextPage()メソッド、previousPage()メソッド、カレントレコード、recordNumberプロパティ、recordCountプロパティの関係
さて、最後の実験だ。先に、繰り返しテーブルでDATAPAGESIZE属性を指定すれば、一度に表示するレコード数を制限できることを紹介した。そして、nextPage()とpreviousPage()メソッドを使って、ページを切り替えられることも紹介した。ここでも、やはりいくつかの疑問が生じる。
まず、一度に表示するレコード数を制限したとき、レコード総数のrecordCountプロパティはいくつになるだろう。表示されているレコード数になるだろうか。あるいは、表示されていないレコードも含めた総数になるだろうか。
次に、nextPage()とpreviousPage()メソッドでページを切り替えたとき、カレントレコードはどうなるだろうか。たとえば、読み込んだ直後は、先頭レコードの「ホンダのアコード」がカレントレコードだ。そして、nextPage()メソッドで次ページに切り替えたら、表示上の先頭レコードが「マツダのデミオ」になったとする。このとき、カレントレコードは「ホンダのアコード」「マツダのデミオ」のどらだろうか。
同じく、recordNumberプロパティも疑問だ。読み込んだ直後は、1、2、3……となるのは分かる。では、nextPage()メソッドで次ページを表示したとき、表示上の先頭レコードのrecordNumberプロパティは1になるだろうか。それとも、前ページからの続きの番号になるだろうか。
次のサンプルが、こうした疑問の答えだ。
【サンプル19:smp019.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function checkTotal() {input1.value = carData.recordset.recordCount}
/SCRIPT>
/HEAD>
BODY onload="checkTotal()">
BUTTON onclick="table1.previousPage()"><-</BUTTON>
BUTTON onclick="table1.nextPage()">-></BUTTON><BR>
recordCount=<INPUT ID="input1" SIZE=2></INPUT><BR>SPAN DATASRC="#carData" DATAFLD="name"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="maker"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="color"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="year"></SPAN>:
SPAN DATASRC="#carData" DATAFLD="price"></SPAN>
TABLE ID="table1" DATASRC="#carData" DATAPAGESIZE=5 BORDER=1 STYLE="font-size:9pt">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR STYLE="cursor:hand;" onclick="alert(this.recordNumber)">
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
2つのボタンはpreviousPage()とnextPage()メソッドでページを切り替える。その下が、レコード総数のrecordCountプロパティの値、その下がカレントレコードだ。各レコードをクリックすると、そのレコードのrecordNumberプロパティがアラート表示されるのは、これまでのサンプルと同じだ。このサンプルで分かることは、次のとおり(実際に試してほしい)。
DATAPAGESIZEで一度に表示するページ数を制限しても、nextPage()メソッド、previousPage()メソッドでページを切り替えてもカレントレコード、recordCountプロパティ、recordNumberプロパティは変化しない。
つまり、並び替えや絞込とは異なり、DATAPAGESIZEの指定、nextPage()とpreviousPage()によるページ切替は、カレントレコード、recordCountプロパティ、recordNumberプロパティにいっさい影響を与えないのである。これも、データバインドを活用していくうえで、重要な知識になるだろう。
PART7 活用してみよう
最終回は、これまでの知識を応用して、より実践的なページを作ってみる。作成したのは、「中古車データベースのデモ」と「7桁郵便番号検索~東京編」の2つだ。あくまでサンプルだから、ソースは短く、スクリプトも大げさではない。にもかかわらず、2つのサンプルからは、応用・活用のアイデアがいろいろ思い浮かぶのではないかと思う。
■中古車データベースのデモ
このページでは、繰り返しテーブルを使っている。絞込や並び替えは用意していない。ポイントは、レコードをクリックすると背景が赤で文字が白になり、マーキー表示の文章も、クリックしたレコード(クルマ)の紹介文に切り替わることだ。これには、recordNumberプロパティが重要な役割を果たしている。まずは、ソースを示そう。
【サンプル20:smp020.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
var i = 1
function chStyle() {table1.rows[i].style.backgroundColor = "white" table1.rows[i].style.color = "black" i = carData.recordset.AbsolutePosition table1.rows[i].style.backgroundColor = "red" table1.rows[i].style.color = "white"}
function chRecord(select_record) {carData.recordset.AbsolutePosition = select_record.recordNumber chStyle()}
/SCRIPT>
/HEAD>
BODY onload="chStyle()">
CENTER>
MARQUEE STYLE="background-color:blue;color:white;font-size:18pt;"
DATASRC="#carData" DATAFLD="introduction">/MARQUEE>
P>
TABLE ID="table1" DATASRC="#carData" BORDER=1 STYLE="font-size:9pt;width:500;">
THEAD>
TR BGCOLOR="silver">
TD>車名</TD>
TD>メーカー</TD>
TD>色</TD>
TD>生産年</TD>
TD>価格</TD>
/TR>
/THEAD>
TBODY>
TR STYLE="cursor:hand;" onclick="chRecord(this)">
TD><SPAN DATAFLD="name"></SPAN></TD>
TD><SPAN DATAFLD="maker"></SPAN></TD>
TD><SPAN DATAFLD="color"></SPAN></TD>
TD><SPAN DATAFLD="year"></SPAN></TD>
TD><SPAN DATAFLD="price"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
/CENTER>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=carData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="car_long.txt">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
データソースとして指定したのは、car_long.txtというカンマ限りのテキストファイルだ。その中身は、次のようになっている。
name,maker,color,year:Int,price:Int,introduction
アコード,ホンダ,白,92,800000,"ワンオーナー車。丁寧に使われていたので、程度はかなりのモノ。お買い得車。"
マークⅡ,トヨタ,白,94,1500000,"トヨタの定番高級車。エンジン、内装ともに最上ランク。ちょっと高級志向のあなたに!"
カムリ,トヨタ,黒,90,550000,"黒のカムリは希少。オヤジ車と侮るべからず!"
: :
これまでのサンプルで使ったcar.txtとのちがいは、introductionという項目を追加していることだ。この項目のデータは、各クルマの紹介文だ。繰り返しテーブルでintroduction項目のデータを表示することもできるが、そうすると、表が横長になってしまう。そこで、このintroduction項目のデータはマーキーとして表示してある。
スクリプトを見ていこう。まず、グローバル変数iには「1」が入る。そして、ページが読み込まれると、chStyle()関数が実行される。chStyle()関数の定義は次のとおりだ。
function chStyle() {
table1.rows[i].style.backgroundColor = "white" table1.rows[i].style.color = "black" i = carData.recordset.AbsolutePosition table1.rows[i].style.backgroundColor = "red" table1.rows[i].style.color = "white"
}
ここでポイントなのが、rowsという配列だ。table1は繰り返しテーブルのIDだが、rowsという配列を使うことで、この表の行を特定できる。たとえば、table1.rows[0]なら先頭行、table1.rows[1]なら2行目……といった具合(0から始まることに注意)。これを利用して、各行のスタイルであるbackgroundColor(背景色)とcolor(文字色)を指定している。ページ読込直後はiに1が入っているから、
table1.rows[1].style.backgroundColor = "white"
table1.rows[1].style.color = "black"
によって、2行目のレコードの背景が白、文字が黒になる。次に、変数iには、
i = carData.recordset.AbsolutePosition
によってカレントレコードの番号が入る。読込直後はカレントレコードは先頭、つまり1番のレコードだから、iには1が入る。そして、
table1.rows[1].style.backgroundColor = "white"
table1.rows[1].style.color = "black"
によって、2行目の同じレコードの背景が赤、文字が白になる。
なんでこんな面倒なことをしているかというと、このchStyle()関数は、繰り返しテーブルの各レコードをクリックしたときにも実行しているからだ。レコードをクリックすると、そのレコードの背景を赤、文字を白にすると同時に、1つ前のレコードの背景色を白、文字を黒に戻さなければならない。この動きを理解するために、各レコードをクリックしたとき実行されるchRecord(select_record)関数を見てみよう。chRecord(select_record)関数の定義は次のとおりだ。
function chRecord(select_record) {
carData.recordset.AbsolutePosition = select_record.recordNumber chStyle()
}
この関数は、レコードをクリックすると、chRecord(this)の形式で実行されるため、引数のselect_recordには、クリックされたレコードそのものが入る。そして、そのレコードのrecordNumberプロパティを、カレントレコードの番号に入れる。これによって、クリックしたレコードがカレントレコードになる。その後、chStyle()関数を実行する。仮に、5番目のレコードをクリックしたとしよう。すると、カレントレコードが5番目になったあと、再びchStyle()関数が実行される。chStyle()関数の動きを見てみよう。
まず、iにはまだ1が入っているはずだ。だから、先頭のレコードは背景色が白、文字が黒に戻る。その後で、iにはカレントレコードの番号である5が入る、そして、5番目の(つまりクリックした)レコードの背景が赤、文字が白になる。
以上が、クリックしたレコードだけ色が変化する仕掛けだ。では、マーキーはどうだろう。マーキーの記述は次のとおりだ。
MARQUEE STYLE="background-color:blue;color:white;font-size:18pt;"
DATASRC="#carData" DATAFLD="introduction">/MARQUEE>
ここには何の仕掛けもない。カレントレコードのintroduction項目のデータが表示されるだけだ。繰り返しテーブルでレコードをクリックすると、そのレコードがカレントレコードになるから、マーキーにも、そのレコードのintroduction項目のデータが表示されるにすぎない。
さて、このサンプル、クルマの画像ファイルを使えば、もっと実用的になりそうだ。レコード数がたくさんあれば、ページを切り替えられるようにしたり、絞込、並び替えの機能を盛り込んでもいい。データソースのテキストファイルは、Excelなどで簡単に作成できるから、データを更新するのもそれほど手間ではないだろう。
■7桁郵便番号検索~東京編
今度は、絞込を中心にしたサンプルだ。東京都の5桁の旧郵便番号、7桁の郵便番号、住所1(区名)、住所2(町名、ビル名など)に条件を設定し、レコードを絞り込める。たとえば「住所1」に「練馬区」と入力して<絞込>ボタンをクリックすれば、練馬区のレコードだけが表示される。文字は一部でかまわない。たとえば「住所2」に「神」と入力して絞り込めば、住所2に「神」という文字を含むレコードだけが表示される。「住所1」を「練馬区」、「住所2」を「神」にして、練馬区のうち住所に「神」という文字を含むレコードといったand検索も可能だ。
絞込を解除するなら<絞込解除>ボタンをクリックし、<、>ボタンでページを切り替えられる。また、絞り込まれたレコード数が「絞り込まれたレコード数/総レコード数」の形式で表示される。ソースは次のとおり。
【サンプル21:smp021.html】
HTML>
HEAD>
TITLE></TITLE>
SCRIPT LANGUAGE="JavaScript">
function filterRec() {var check_cond = input1.value + input2.value + input3.value + input4.value if(check_cond != "") { var cond1 = "old_num=" + "*" + input1.value + "*" var cond2 = "new_num=" + "*" + input2.value + "*" var cond3 = "address2=" + "*" + input3.value + "*" var cond4 = "address3=" + "*" + input4.value + "*" var cond_all = cond1 + "&" + cond2 + "&" + cond3 + "&" + cond4 tokyoData.object.Filter = cond_all tokyoData.Reset() checkCount() }}
function filterReset() {
input1.value = "" input2.value = "" input3.value = "" input4.value = "" tokyoData.object.Filter = "" tokyoData.Reset() checkCount()
}
function checkTotal() {
span1.innerText = tokyoData.recordset.recordCount span2.innerText = tokyoData.recordset.recordCount
}
function checkCount() {
span1.innerText = tokyoData.recordset.recordCount
}
/SCRIPT>
/HEAD>
BODY onload="checkTotal()">
CENTER>
BUTTON onclick="table1.previousPage()"><</BUTTON>
BUTTON onclick="table1.nextPage()">></BUTTON>
BUTTON onclick="filterRec()">絞込</BUTTON>
BUTTON onclick="filterReset()">絞込解除</BUTTON>
[<SPAN ID="span1"></SPAN>/
SPAN ID="span2"></SPAN>]
TABLE ID="table1" DATASRC="#tokyoData" DATAPAGESIZE=15 STYLE="font-size:9pt;">
THEAD>
TR STYLE="background-color:navy;color:white;">
TD ALIGN="center">旧郵便番号<BR><INPUT ID="input1" SIZE=8 STYLE="font-size:8pt;"></INPUT></TD>
TD ALIGN="center">7桁郵便番号<BR><INPUT ID="input2" SIZE=10 STYLE="font-size:8pt;"></INPUT></TD>
TD ALIGN="center">住所1<BR><INPUT ID="input3" SIZE=10 STYLE="font-size:8pt;"></INPUT></TD>
TD ALIGN="center">住所2<BR><INPUT ID="input4" SIZE=30 STYLE="font-size:8pt;"></INPUT></TD>
/TR>
/THEAD>
TBODY>
TR STYLE="background-color:silver">
TD><SPAN DATAFLD="old_num"></SPAN></TD>
TD><SPAN DATAFLD="new_num"></SPAN></TD>
TD><SPAN DATAFLD="address2"></SPAN></TD>
TD><SPAN DATAFLD="address3"></SPAN></TD>
/TR>
/TBODY>
/TABLE>
/CENTER>
!-- データベース用オブジェクトの指定 -->
OBJECT ID=tokyoData CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
PARAM NAME="DataURL" VALUE="tokyo.csv">
PARAM NAME="UseHeader" VALUE="true">
/OBJECT>
/BODY>
/HTML>
用意するデータソースのテキストファイルのtokyo.csvは、次のような中身をしている。
old_num:STRING,new_num:STRING,address_f1:STRING,address_f2:STRING,address_f3:STRING,
=>address1:STRING,address2:STRING,address3:STRING
100,1000000,トウキョウト,チヨダク,イカニケイサイガナイバアイ,東京都,千代田区,以下に掲載がない場合
102,1020072,トウキョウト,チヨダク,イイダバシ,東京都,千代田区,飯田橋
102,1020082,トウキョウト,チヨダク,イチバンチョウ,東京都,千代田区,一番町
101,1010032,トウキョウト,チヨダク,イワモトチョウ,東京都,千代田区,岩本町
101,1010047,トウキョウト,チヨダク,ウチカンダ,東京都,千代田区,内神田
100,1000011,トウキョウト,チヨダク,ウチサイワイチョウ,東京都,千代田区,内幸町
: :
繰り返しテーブル、一度に表示するレコード数、ページの切替、そして、絞込とその解除方法については、これまでに紹介したので繰り返さない。このページのポイントは、4つの入力フォームに入力された文字をAND条件で絞り込む仕掛け、および絞込を行ったときに、絞り込まれたレコード数を表示する仕掛けの2つだ。
まず、入力フォームには<INPUT>を使っている。たとえば旧郵便番号の入力箇所の記述は次のようになっている。
TD ALIGN="center">旧郵便番号<BR><INPUT ID="input1" SIZE=8 STYLE="font-size:8pt;"></INPUT></TD>
つまり、「旧郵便番号」という文字と<INPUT>を1つのセル内にいっしょに書き込み、<BR>タグで2行にしているにすぎない。<INPUT>タグのIDはinput1だ。同様に、「7桁郵便番号」にはinput2、「住所1」にはinput3、「住所2」にはinput4という<INPUT>タグを入れてある。
絞込>ボタンをクリックすると、filterRec()関数が実行される。filterRec()関数の定義は次のとおりだ。
function filterRec() {
var check_cond = input1.value + input2.value + input3.value + input4.value
if(check_cond != "") { var cond1 = "old_num=" + "*" + input1.value + "*" var cond2 = "new_num=" + "*" + input2.value + "*" var cond3 = "address2=" + "*" + input3.value + "*" var cond4 = "address3=" + "*" + input4.value + "*" var cond_all = cond1 + "&" + cond2 + "&" + cond3 + "&" + cond4 tokyoData.object.Filter = cond_all tokyoData.Reset() checkCount() }
}
まず、先ほどの4つの<INPUT>タグのvalue、つまり入力された文字が連結されて変数check_condに入る。そして、変数check_condが空かどうか調べる。空なら何もしない。空でなければ、つまり何か4つの<INPUT>タグに何かが入力されていれば、if文の内部が実行される。
たとえば、「住所1」に「練馬」と入力し、他に何も入力しないで<絞込>ボタンをクリックすると、変数cond1~cond4には次のような文字列が入ることになる。
cond1……old_num=**
cond2……new_num=**
cond3……address2=練馬
cond4……address3=**
変数cond_allは、この4つの変数を"&"で連結した文字列が入るから、この場合なら、次の文字列が入ることになる。
old_num=**&new_num=**&address2=練馬&address3=**
あとは、この変数old_numを条件に絞込を行っているだけだ。**はワイルドカードで、けっきょくすべてのレコードを意味することになるから、address2=練馬という条件にひっかかるレコードだけが絞り込まれることになる。以上が、複数の条件をANDで結んで絞り込む仕掛けだ。
そして、絞り込んだあとには、checkCount()関数が実行される。定義は次のとおりだ。
function checkCount() {
span1.innerText = tokyoData.recordset.recordCount
}
前回、絞込を行うと、レコード総数を示すrecordCountの値は、絞り込んだ後のレコード数になることを紹介した。要するに、checkCount()関数は、絞り込んだあとでspan1というIDの<SPAN>タグの中の文字を、このrecordCountにしているだけだ(innerTextについて分からない方は調べてほしい)。
なお、ページを読み込んだ直後には、checkTotal()関数も実行している。定義は次のとおりだ。
function checkTotal() {
span1.innerText = tokyoData.recordset.recordCount
span2.innerText = tokyoData.recordset.recordCount
}
これで、span1とspan2に総レコード数が表示される。span2の方は、このあと数値が変化することはない。総レコード数をつねに表示しておきたいからだ。これに対し、span1の方は、絞込が行われるごとに先ほどのcheckCount()関数が実行されるから、絞り込まれたレコード数へと変化することになる。
■より高度なトピック
さて、長々と解説してきたデータバインドも、とりあえずこれでおしまいだ。ただし、まだまだ解説していなかったり、ボクもよく分かっていないトピックがウンとある。
たとえば、レコードを追加したり削除することもできる。あるいは、特定のレコードをマークして、一時的にメモリに記憶しておくこともできるようだ。
さらに、データソースであるテキストファイルの読込状態をチェックするイベントハンドラもある。データバインドでは、HTMLファイルとデータソースであるテキストファイルをサーバから読み込み、そのあとクライアントで処理を行うわけだが、テキストファイルが大きいと、やはり読込に時間がかかってしまう。完全に読み込む前にユーザーが絞込などを行うと、エラーになる可能性があるわけだ。そこで、その回避策として、こうしたイベントハンドラが用意されているのだ。
他にも、まだあるので、関心のある方は調べてみよう。もしも要望があれば、筆者も調べてみたいが、まぁ、要望はないだろうなぁ(まず、ここまで読む人はいない気もする。予想が外れればいいんたけれど)。
そういえば、最近マイクロソフトのHTMLエディタであるFrontPage98を購入し、使ってみた。立派に、データバインドも利用できる。ただし、使い方がよく分からない。データバインドの基本が分かっていないと、使いこなせないのではないかと思う。
さて、このデータバインド、IE4のDynamicHTMLの中では、商利用やIE4をクライアントとするイントラネットをターゲットにしている感が強い。実際、Webページの見た目をどうこうするタイプの機能ではないので、個人の一般ユーザーのあいだでは、あまり盛り上がらないだろう。が、見てのとおり、可能性と実用性はある。良くも悪くも、Windows98は普及し、どのマシンでもIE4が走っている時代がくるだろう。あと2~3年もすれば、データバインドが注目を浴び始めるだろうとボクは思っている。