JavaScriptと活用しよう

JavaScriptの有効利用法,CGIプログラミングの支援ツールとして考える

戻る

JavaScriptは,Webブラウザのバージョンによって,動作が異なる場合があり, いろいろなWebブラウザを利用してアクセスする可能性があるシステムで不用意に 利用すると,トラブルの元になります. しかし,どのWebブラウザでも利用できる最小限の機能や,イントラネットで利用し, Webブラウザを特定できる場合は,CGIに対して最小限のリクエストで最大限の効果を 得られるシステムを構築する手助けになるでしょう.
JavaScriptは、HTMLと共にクライアントに送信されるため,内部処理を利用者に 知られたくない場合は,利用しないほうがよいでしょう.

8.1 文字列の長さのチェックを行う

HTMLでは,文字列の長さを「バイト数」でなく「文字数」で扱う傾向があります.
しかし,ほとんどの場合CGIで処理できるサイズには,制限が必要になります. そこで,<INPUT>タグにsize や maxlength を指定して入力できるサイズをCGIに 引き渡す前に制限してしまうことによりデータ長制限を越えて入力され,処理が正しく 行えないことが明らかなリクエストを制限することが可能になります.
しかし,<INPUT>タグの制限は,Webブラウザによって制限するデータ長が, 文字数のものやバイト数のものがあり,HTMLの記述だけでは明確なチェックが できません.
JavaScript の文字列を格納するstringオブジェクトには,length 変数がありますが, この変数はバイト長でなく文字数を返却します.
そこで,必ずバイト長でチェックできる関数を作成して,送信する前にチェックを 行い,制限を越えている場合は送信を行えないようにします.
文字列に設定されている文字コードはcharCodeAtメソッドで取得することができます. このメソッドは,JavaScript1.2から追加されていますので,古いバージョンの Webブラウザでは動作しない場合があります.このメソッドで取得した値が,0~255 であれば,1バイトと判断できますが,1つ注意があります.
0xa1 ~ 0xdfはシフトJISでは半角カナですが,このコードをEUCやJISコードに変換 する場合,7ビットASCIIコードではありませんので単純に1バイトコードと判断できない 場合があります.ということで,文字コードが0~127を1バイトコードと判断するほうが よいでしょう.この関数を応用して,文字列内に半角カナが含まれているかをチェック して,半角カナを入力できないようにするのもよいでしょう.


function StrLen(str) { var ct; var size = 0; for(ct = 0; ct < str.length; ct++) { var c = str.charCodeAt(ct); if(c >= 128){ size++; } size++; } return size; }

リスト1:文字列の長さを返す

8.2 文字列を比較する

ありそうで存在しないのが,文字列比較メソッドです.
少し強引ですが,文字列を配列に設定して,sortメソッドでソートを行い,配列の ソート順をチェックすることで代用することも可能です.しかし,単純に文字列変数 内の値と固定文字列を比較したいときは,もっと簡単でシンプルな方法で文字列の 比較を行いたくなります.
ということで,strcmp関数のJavaScript版を作成してみました. ちょっとC言語病でしょうか?!


function StrCmp(str1, str2) { var ct; var cmp; if((cmp = str1.length - str2.length) != 0){ return cmp; } for(ct = 0; ct < str1.length; ct++) { var c1 = str1.charCodeAt(ct); var c2 = str2.charCodeAt(ct); if((cmp = (c1 - c2)) != 0){ break; } } return cmp; }

リスト1:文字列の比較

8.3 選択した項目のURLを開く

アンカーやボタンをクリックしてCGIを実行するだけでは,芸がありませんので, ドロップダウンリストから選択した項目を実行するのもよいでしょう(リスト3).
Netscape Communicator は,<SELECT>タグの onChange イベントは動作しない バージョンがありますのでボタンで実行できるようにしておく必要があります. [表示する]


<script language="JavaScript"> <!-- function JumpUrl(form) { if(form.engine.value != ''){ window.open(form.engine.value, "_blank", "toolbar=yes,location=yes,directories=yes,status=yes," +"menubar=yes,scrollbars=yes,resizable=yes"); } form.engine.selectedIndex = 0; // 初期状態に戻す return false; } //--> </script> </head> <body bgcolor="white"> <center> 検索エンジンのHPを開く <form method="GET" onSubmit="return JumpUrl(this);"> <td><select name="engine" onChange="JumpUrl(this.form)"> <option value="" selected>検索エンジンを選択してください <option value="http://www.yahoo.com/"> ├ Yahoo!(US) <option value="http://www.yahoo.co.jp/"> ├ Yahoo!(日本) <option value="http://search.biglobe.ne.jp/index-netplaza.html"> ├ BIGLOBEサーチ <option value="http://www.goo.ne.jp/"> ├ goo <option value="http://www.google.ne.jp/"> ├ google <option value="http://www.infoseek.co.jp/"> ├ InfoSeek <option value="http://www.excite.co.jp/"> ├ Excite Japan <option value="http://www.lycos.co.jp/"> ├ Lycos Japan <option value="http://www.fresheye.com/"> ├ フレッシュアイ <option value="http://infonavi.infoweb.ne.jp/"> ├ InfoNavigator <option value="http://whois.nic.ad.jp/cgi-bin/whois_gw"> └ JPNIC Whois Gateway </select></td> <td>を <input type="submit" value="開く"></td> </form>

リスト3:選択した項目のURLを開く

8.4 クエリ文字列を取得する

JavaScriptでは,自分のURLに関する情報をlocationオブジェクトから取得する ことができます(表1).
これらの情報を利用することで,ユーザが画面から入力した値を利用して, 動的に画面を変更するなどの処理を行うことが可能です.
この中で,外部からの値を受け取る方法としてクエリ文字列を利用することが できます.クエリ文字列は,CGIなどの動的にHTMLを生成することができるオブジェクト だけでなく,HTMLでもスクリプト言語を利用することで, 取得することができます(リスト4). [表示する]

hashアンカー名
hostURLのホスト名とポート名
hostnamURLのホスト名部分
hrefURL
pathnameURLのパス名部分
portURLのポート名
protcolURLのプロトコル部分
searchURLのクエリ(問い合わせ)文字列
targetURLのトランジット属性
表1:locationオブジェクトで取得できる値


//--------------------------------------------------------------------------- // 変数 //--------------------------------------------------------------------------- var Name = new Array(); var Value = new Array(); var Keys = 0; //--------------------------------------------------------------------------- // 変数を追加する(内部用) //--------------------------------------------------------------------------- function AddString(name, value) { Name[Keys] = name; Value[Keys] = value; Keys++; } //--------------------------------------------------------------------------- // 指定された変数の登録番号を返す(内部用) //--------------------------------------------------------------------------- function SearchString(name) { var num = -1; for(ct = 0; ct <= Keys; ct++){ if(name == Name[ct]){ num = ct; break; } } return num; } //--------------------------------------------------------------------------- // 指定された変数の値を返す //--------------------------------------------------------------------------- function GetString(name) { var value = ''; if((num = SearchString(name)) >= 0){ value = Value[num]; } return value; } //--------------------------------------------------------------------------- // 変数を追加・修正する //--------------------------------------------------------------------------- function SetString(name, value) { if(name.length == 0){ return 0; } if((num = SearchString(name)) >= 0){ Value[num] = value; } else{ AddString(name, value); } return 0; } //--------------------------------------------------------------------------- // 変数を削除する //--------------------------------------------------------------------------- function DeleteString(name) { if((num = SearchString(name)) >= 0){ Name[num] = ''; Value[num] = ''; } return 0; } //--------------------------------------------------------------------------- // クエリ文字列を解析する //--------------------------------------------------------------------------- function CreateQuetyStringList() { var name = ''; var value = ''; var flag = false; for(ct = 1; ct <= location.search.length - 1; ct++){ ch = location.search.charAt(ct); if(ch == '?'){ flag = false; continue; } if(ch == '='){ flag = true; if(value.length > 0){ name = value; } value = ''; continue; } if((ch == '&') || (ch == '#')){ flag = false; if((name.length > 0) && (value.length > 0)){ AddString(name, value); } name = ''; value = ''; continue; } value += ch; } if(value.length > 0){ if(flag == true){ AddString(name, value); } } return 0; }

リスト4:クエリ文字列の値をを名前指定で取得するための関数

8.5 Internet Explorer の「お気に入り」に独自のアイコンを設定するには

ホームページのURLを「お気に入り」に追加すると,そのページ固有のアイコンを 表示することがあります.
このアイコンは,IEが識別できるファイル名でWebサーバ上に置くことで, 「お気に入り」への追加を行ったときに,Internet Explorer が自動的にダウンロード を行います.

●サーバ全体に1つのアイコンを設定する場合

ユーザーが「お気に入り(favorites)」を登録したときに,ユーザーが使っている Webブラウザの,インターネットショートカットの横にオリジナルのロゴを表示 させたい場合,オリジナルのアイコンファイルを "favicon.ico" という名前で ドメインの root ディレクトリに配置しておきます. http://www.domain.ne.jp/favicon.ico

このような配置を行った場合 "domain.ne.jp" 内のページがお気に入りに追加される と,原則としてすべてこのアイコンファイルが使用されます.
http://www.hogehoge.ne.jp/~foo/favicon.ico
このような配置を行った場合,ユーザー "foo" 配下にあるすべてのページについて, お気に入りに追加された際には原則としてこのアイコンファイルが使用されます.
http://www.hogehoge.edu/introduce/favicon.ico
このような配置を行った場合,URI "introduce/" 配下のページについて,お気に入り に追加された際には原則としてこのアイコンファイルが使用されます.
同一ディレクトリ内でページごとに異なるアイコンを使用したい場合には,例えば 次のようなタグをページの中に加えることで,他の場所に置くことが可能です.
<LINK REL="SHORTCUT ICON" href="/iconfile/hogehoge.ico">
このタグが記述されたページがお気に入りに追加された場合には,同一ディレクトリ, または上位ディレクトリ内に "favicon.ico" が存在するかどうかにかかわらず "/iconfile/hogehoge.ico" がこのページのお気に入りのアイコンとして 使用されます.

8.6 Webページから「お気に入り」への追加を行うには

JavaScriptを利用することにより,追加を支援することができます. ただし,IE4.0以上でなければ動作しません. 以下のボタンを押すと,お気に入りへの追加ができます. [表示する]


<script language="JavaScript"> <!-- function AddFavorite(url, title) { if((navigator.appVersion.indexOf("MSIE") > 0) &&(parseInt(navigator.appVersion) >= 4)){ window.external.AddFavorite(url, title); } else{ alert("IE4.0以上でサポートしています."); } } //--> </script> <a href="favorite.htm" onClick='AddFavorite(location.href, document.title); return true;'> お気に入りに追加! </a>

リスト5:お気に入りに追加する

8.7 必須入力項目に値が入力されているかを調べる

入力項目に値を設定して実行しなければCGIが正しく動作しない場合, スクリプトでチェックし値が設定されていない場合はリクエストを行わない ようにすることで,実行しても正しい結果が得られないことが明らかな無駄な リクエストを行わないようにすることができます.
また,この時点で数値文字列の入力項目に,それ以外の文字の入力するのをを禁止 したり,半角カナなどの入力すると問題になりそうな文字列を排除したり警告を発する のもよいでしょう.
なにかと便利に利用できるJavaScriptですが,スクリプトの動作はWebブラウザに よって動作やサポートする機能に違いがあり,CGIにチェックを行った正しい リクエストが送信できることを保証することはできません.仮に利用するWebブラウザ を制限しでも,通信の障害で正しい情報が受信できない場合も考えられます.
したがって,JavaScriptでの入力文字列のチェックは,あくまでも補助的な機能と 考え,送信しても必ず失敗すると思われる無意味なリクエスト行わないようにすること を目的としたほうがよいでしょう.
スクリプトで正しい値が入力されるように制御したとしても,CGI側では値をチェック し,不正な値があっても適切なエラー処理を行うように設計しなければ なりません.[表示する]


<script language="JavaScript"> <!-- function CheckItems(form) { if(form.text.value == ''){ alert('値を入力してください.'); return false; } return true; } //--> </script> <form method="GET" onSubmit="return CheckItems(this);"> 入力<input type="text" name="text" value=""> <input type="submit" value="送信"></center> </form>

リスト6:入力項目に値が設定されているかをチェックする


8.8 入力域をdisableにする

項目の値を動的に変更を許可したり禁止させる場合に利用できます. [表示する]

●Webブラウザ固有の方法

入力域を無効(disable)にするには,Internet Explorer 4.01以降の場合, HTMLの記述で実現できます(リスト7).
固定の場合は,リスト8のような記述もできます.


<INPUT type="radio" name="sw" onClick="text1.readOnly=true; text1.focus();">ReadOnly <INPUT type="radio" name="sw" onClick="text1.readOnly=false; text1.focus();" checked>Edit <INPUT type="text" name="text1">

リスト7:入力域の無効(Internet Explorer 4.01以降)



<INPUT TYPE=SUBMIT VALUE="Submit Data" DISABLED ID=btnSubmit> <INPUT TYPE=TEXT VALUE="This is read only" READONLY>

リスト8:値が固定の場合の入力域の無効(Internet Explorer 4.01以降)

●Webブラウザに依存しない方法

Webブラウザに依存しないHTMLにより,入力域を無効にする方法は存在しません. しかし,JavaScriptを利用することにより,「無効になっているように見せかける」 ことができるようになります.
Netscape Comunicator 4.xx, Netscaoe6.1, InternetExplorer4.xx, 5.xxでは 動作しますが,Netscape6だけは,正しく動作しません.



<script language="JavaScript"> <!-- function RadioDisable(form) { // 強制的にボタン1を選択させる form.rd[0].checked = true; form.rd[0].focus(); } //--> </script> <form name="TestForm"> <input type="radio" name="rd" value="1" checked>ボタン1 <input type="radio" name="rd" value="2" onClick="RadioDisable(TestForm);"> ボタン2 </form>

リスト9:強制的に値を入れ替える


8.9 ボタンの連打を防ぐ

利用者が,Webブラウザに表示したボタンを連打すると,押された回数分のリクエスト が発生し,Webサーバへの負荷が増加することになります.
また,ネットワークの遅延などでレスポンスが遅れた場合には,本当に動作している のかが不安になって,ついボタンを何度も押してみたくなりますし,レスポンスが 誤って連打してしまった利用者は,リクエストが終了するまで次の操作を待たされる ことになります.利用者のボタンの連打は,簡単なスクリプトを利用することで 防止することが可能です.
そこで,スクリプトを使って,一回押したボタンを2回目以降に押した場合は無効 にしてしまいます.
ボタンオブジェクトのdisabledメンバー変数は,IE4.01以降,Netscape6以降で利用可能 ですが,対応していないバージョンでも連打を防ぐため,実行フラグを用いて処理が 何回も実行されないようにしておく必要があります. [表示する]


<script language="JavaScript"> <!-- var fExec = false; function OneTimeAction(form) { if(fExec == true){ return false; // 2回目以降は処理を行わない } fExec = true; alert("処理を行う.") form.test.value = "もうだめよ"; form.test.disabled = true; // IE4.01以降,Netscape6以降で有効 return false; } //--> </script> <body bgcolor="white"> <form method="GET" onSubmit="return OneTimeAction(this);"> 1回しか反応しないボタン→<input type="submit" name="test" value="1回だけよ"> </form> </body> </html>

リスト10:1回しか反応しないボタン

8.10 JavaScriptとHTMLを混在させるときの注意点

●スクリプトの記述方法による問題

JavaScriptをHTML文書内に埋め込んだときに,HTML記述が正しく表示されない 場合があります. このような現象が起こった場合,<BODY>タグで囲まれた 記述部分に記述しているある<SCRIPT>タグをチェックする必要があります.
Webブラウザは,以下のような手順でHTMLファイルを解析します.

1)<HEAD>タグはすべての読み込みを行ってから解析を開始する.
2)<BODY>タグはデータを受信しながら解析が可能なだけのデータを受け取ると解析を開始し,<BODY>タグの解析はデータの受信と並列に行われている.

このため,HTMLが不十分な状態で解析される場合が発生し,最終的に出力される HTMLが正しくない状態になる場合があります.
この場合,スクリプト部分は完全に読み込まれてから解析させるように 記述すれば解決する可能性があります.

1)HTMLで記述できる部分は極力HTMLのみで記述する.
2)<BODY>タグ内にスクリプトを記述する場合は,最小限の記述に留める.
3)関数を記述する場合は,<HEAD>タグ内に記述する.(JSファイルを読み込む場合も同様)
4)スクリプトを多く使う必要がある場合はHTML記述を行わず,すべてスクリプトでHTMLを出力させる.このとき,HTMLの出力には,document.writeln() でなく,document.write() を使いましょう.Webブラウザのバージョンによっては,以下のような問題が発生する場合があります.
  • 256バイトの文字列を設定すると,スクリプトエラーが発生する場合がある.
  • 予期しない場所に改行コードが設定される場合がある.


    <html> <meta http-equiv="Content-Type" content="text/html; charset=x-sjis"> <head> <title>タイトル</title> <link rel='stylesheet' type='text/css' href='main.css'> <script language="JavaScript" src='main.js'></script> </head> <body background="main.jpg" bgproperties="fixed" > <script language = "JavaScript"> <!-- displayHtml(); //--> </script> </body> </html>

    リスト11:スクリプトだけで表示するHTML



    function displayHtml() { document.write('XXXX<br>') document.write('XXXX<br>') : : : }

    リスト12:HTMLを出力するスクリプト

    8.11 Webブラウザが完全に日本語対応していない(またはバグがある)

    Internet Explorer 3.x のJavaScriptは,日本語が使えないことで有名でしたが, いまでも日本語が正しく処理できないものが存在しますし,海外で開発されたものは バージョンにかかわらず,このような問題を持っている可能性があります.

    ●考えられる原因

    JavaScriptで日本語を扱う場合,日本語対応が完全でないWebブラウザで表示した ときは,Webブラウザ自体にバグがあるときには正しく動作しない場合が あります.
    そのようなWebブラウザでアクセスが行われることを想定したシステムを構築する ときには,以下のような対策が考えられます.

  • JavaScriptに対応していないWebブラウザ用のページを用意する.
  • document.write で日本語を利用する場合は,JavaScriptを記述したファイルをHTMLファイル内に直接記述する.(HTMLとJavaScriptを別ファイルに記述すると,文字コードの識別が正しく行われず,UTF-8 で文字列を出力してしまうバージョンが存在する)
  • 消極的な方法ですが,問題のあるWebブラウザは対応外とするか,スクリプトでの日本語文字列の使用を避ける.