外部スクリプトに引数を渡す(?)


■このページは

このページは没になったものです。質問者が自己解決しちゃったので掲示板には投稿されませんでした。
でもなかなか面白いこと書いてあるので、再編集ついでに復活。


■Q&A

■Question

JSファイルについて 2003.4.23.Wed No.1694

はじめまして。
JavaScript の外部ファイルについて質問なのですが。

<script language="javascript" src="./testpg.js?stat=aiueo"></script>

このタグの書き方で外部ファイルとしては、ブラウザで読み込まれ testpg.js の処理が実行されていたので「?」以降が「引数として認識している」のではないかなと思ったのですが、「location.search」では、受け取れませんでした。

この様に引数をつけて「?」より後の部分を外部JSファイルで受け取ることは可能でしょうか?

分かる方がいらっしゃいましたら、ご教授ください。よろしくお願いします。

実験環境:MSIE6 & NN7

■Self Res

Re: JSファイルについて 2003.4.24.Thu No.1703

:自己レス:

結局のところ…
もろもろのこともあって、JSファイルの部分をCGIで出力することにしました。
変数をhtmlに記述する方法も考えたんですが、JSファイルが何らかの原因で受け取れなかった場合エラーの回避がしづらいこと判明した為、お蔵入りになりました。^^; 出来ないことは無いんですがソースが長くなるし、めんどくさい(ぉ
CGIにすれば、「?」以降の部分も受け取れますんで…(汗
でも…処理がかなり遠回りになったような気が…(^^;

閑話休題。
今後の参考になるかもしれないんで、方法を書いておきます。(実験段階ですが…)
まずタグは、こんな感じになります。

<script language="javascript" src="./jsoutput.cgi?stat=aiueo"></script>

で、CGI(perl)側で

-- jsoutput.cgi -------------
(省略)
 #JavaScriptですよ、というヘッダ
 print "Content-Type: application/x-javascript\n\n";

 #JavaScript を出力していく
 print "document.write(\"" + $stat +"\");";
 …
(省略)
-----------------------------

注:CGIの掲示板ではないので、CGIの基本的なことや、日本語処理の部分は省略しています。
プログラム中にある $stat は、実行時パラメータ(「?」以降のこと)を変数に割当てたもの。(これも CGIのことなので省略)

■Answer

Re: JSファイルについて ぴろあき 2003.4.24.Thu No.1710

JavaScript における外部ファイルは、その場所に <script></script> を書き、その中に外部ファイル内のスクリプトを全部出力したのと同じことになります。

外部スタイルシートはHTMLから独立しているので、画像の相対パスも外部ファイルから見た場所を示します。しかし JavaScript の場合は違い、あくまでも「HTMLドキュメントの一部」として扱われます。もし外部スクリプトがHTMLから独立していると、「location.search はHTMLのかスクリプトファイルのか」がわからなくなってしまいますよね。

昔、自分も同じことを考えて調べたんですが、外部ファイルが引数を受け取る方法は JavaScript の言語仕様にないようです。
考えてみれば、ブラウザは「スクリプトファイル」を受け取るのではなく、「サーバーに xxx.js?arg=1 をリクエストして返ってきた文字列」を受け取るわけですから、サーバー側が引数を解釈して、それに応じた対応でもしてくれない限り、できないのは当然なのかも。それはつまりCGIということで。

で、それならばと「まるで引数を理解してるかのような動きをする、外部ファイル単体で動く引数受け取りスクリプト」を作ったんですが、Netscape4 で動かないのでお蔵入り的な。
おすそわけしようかとも思ったものの、CGIが使えるのであれば無用の長物です。(結局自分でも使ってないし)

ところで、JavaScript を出力する時のコンテントヘッダって、text/javascript でも text/plain でも問題なく動くとこを見ると、ブラウザはヘッダを見てないんでしょうね。「<script> でリクエストして返ってきたものは全部 JavaScript である」って決め打ちしてんでしょうか。

ってなことを思い、「ならば image/gif で JavaScript を出力してやる!」などと実験したことも。
…いや、なんの問題もなく動いちゃったんですけどね。IE。ほんとにもう。

■Res

Re: JSファイルについて 2003.4.25.Fri No.1716

コンテンツヘッダですが、もし決め打ちしてるとなると、今じゃ殆ど(まったく?笑)見かけなくなってしまった、VisualBasicScript(VBS) が使えなくなってしまうような気が…
確かこれも、外部ファイルに出来ますよね^^;(確証なし)

でも…、VBSのヘッダってちょっと調べたけど見当たらなかったんですよねぇ^^;
WEBサーバ系サイトあたりを調べればあるかもしれませんが…(というか…もう調べる気は無いですけど)

上のことを踏まえると、IE的には、(思いっきり推測ですが)ファイルの内容を調べて実行してるんではないかと思います。

そうなると…

サーバから文字列がくる → ヘッダが変? → ソース調べる → JSかなぁ…<script>だし?? → JSで実行してみる

こんなことをしてたりして…^^;
でかいスクリプトファイルだったらどうするんだろう…??(大汗

■Res

Re: JSファイルについて ぴろあき 2003.4.26.Sat No.1719

いや、そのための language="javascript" であり type="text/vbscript" なのでは…。(笑)
外部スクリプトの場合、<script> 要素にある language なり type なりの属性しか見てないのでしょう…という意味での「<script> でリクエストして返ってきたものは〜」でした。言葉が足りなくてごめんなさい。 この属性を省略した時は <meta http-equiv="Content-Script-Type" content="text/javascript"> が優先され、それもない時はブラウザ依存です。(まぁ普通は JavaScript になるでしょう…というか、属性省略時の <meta> はHTMLの文法において必須です)

ちなみにIEに限らず、コンテントヘッダをちゃんと解釈するブラウザはほとんどないみたいです。文字コードにしても、ヘッダを見ずに <meta> だけ見て、ない時はソースから決め打ち…なんてことをしてるって話です。(真偽のほどは定かでありませんが、動きを見る限り納得できます)

まぁそれはいいとして(いいの?)、先頭にタグがあると拡張子が txt でもHTMLとして解釈してくださるIE様を誰かなんとかしてほしいです。IEって本当に「親切が過ぎる」んですよね。ヘッダにちゃんと text/plain て書いてあるのにー。のにー。

>今じゃ殆ど(まったく?笑)見かけなくなってしまった、VisualBasicScript(VBS)

ブラウザ戦争の最高潮だったVer.3〜4時代、JavaScript の言語仕様を公開した Netscapeと、VBSの言語仕様を頑なに隠し続けた Microsoft。
「Webの世界ではケチなこと言った奴から廃れていく」っつぅ好例となりましたよね。その点、Unisys は実に頭の良い戦法を用いたわけですけど。(もっとも、当時の自分はパソコンなんか触ったこともなかったんですが)

でも、IISとかサーバーサイドスクリプトの分野では、むしろVBSが主流で、JScript の方がマニアックな存在です。WSHでもVBS軍が圧倒的で、JScript 隊の旗色は良くないですし。JScript.Net なんて、存在さえ知られてないかも…。
この辺、「できること」の差がそのまま結果につながってるのでしょう。(クライアントサイドみたいな「環境依存」っていう足枷がない以上、当然の結果かも)

ちなみに、VBSも余裕で外部ファイルにできます。ヘッダも image/png で充分だと思います。どうせ見ないんだから。(苦笑)

ともあれ、今はまだ草案段階の "JavaScript2.0"、今から待ち遠しいです。よだれの出る新機能もいっぱいあるし、スクリプトからの外部スクリプト呼び出し(include)がサポートされるってだけでも幅がかなり広がりますよね。ロード後でも好きな時に他スクリプトを呼べるんですから。(まぁDOMを使えば現時点でもできますけど)
という感じで、JavaScript 話も適当に混ぜておけば万事 all right でしょう。
…本当ですか。


JavaScript を使う方法

■JS版:実行結果

execute.js を、引数を変えながら3回呼び出し、そのリストを書きだしています。
対応ブラウザであれば、ちゃんと引数を識別できていると思います。


■JS版:スクリプトの概要

仕組みとしては、「外部スクリプトが呼び出されると、HTMLパーサはスクリプトエンジンがそのファイルの処理を終えるまで解析を待つ」ことを利用したものです。document.write() で新しいHTMLが出てくるかもしれないからです)
つまり、自分自身が呼び出された時、その時点まで解析できている <script> タグを全部取得すると、一番最後のが自分自身の <script> ということになります。

あとは自分自身の src="" 属性から引数を取得し、条件分岐で処理を実行します。
引数に日本語(2バイト文字)を使いたい場合、escape() で文字コードにしておかないと、思わぬ誤動作の原因になるのでご注意を。

■■■ 注意 ■

非対応ブラウザでも execute.js 自体は読みこみますので、ブラウザ判別はしっかりと。

詳細はソースをご覧ください。
テキストファイルにした execute.js


■JS版:このスクリプトの対応ブラウザ

IE4以降・Netscape6以降・Opera6以降
動作します。
その他のDOM対応ブラウザ(Safariなど)
未確認ですが、動くことが期待されます。
上記以外
動作しません。

CGIを使う方法

■CGIで JavaScript を出力

多くのブラウザで動かしたい場合、CGIを使うこともできます。(多くの講座サイトで解説される「拡張子は .js でないといけない」というのは、厳密には誤りです)

この場合、呼び出す <script>

<script type="text/javascript" charset="Shift_JIS" src="execute.cgi?stat=aiueo"></script>

として、CGIにGETメソッドで引数を判別させ、それに応じたスクリプトを Content-type: application/x-javascript で出力します。(本当は text/javascript が推奨されているのですが、Netscape3 などの古いブラウザで動かなくなります)

この方法だとほとんどのブラウザで実行できるだけでなく、CGIの方でブラウザを判別し、それに適したスクリプトを書きだすことも可能なので、使える環境にあればかなり便利な方法です。(まぁサーバーの負荷が増えるので、サイト全体のスクリプトに使うようなことは遠慮するべきですけど)


■CGI版:ソース

#!/usr/bin/perl

@pairs = split(/[&;]/, $ENV{'QUERY_STRING'});
foreach (@pairs) {
  my($key, $val) = split(/=/);
  $key =~ s/\s//g;
  $pear{$key} = $val; # $pear{'stat'} = 'aiueo'; になる
}

print <<"hereDocument";
Content-type: application/x-javascript

if ("$pear{'stat'}" == "aiueo")
  document.write("引数受け取りOK");

/*
$pear{'stat'}はCGIの中で"aiueo"に展開され、ブラウザには
if ("aiueo" == "aiueo")
  document.write("引数受け取りOK");
として出力される
*/

hereDocument
exit;


■CGI版:動作例

CGIを直接呼んでみると出力結果を見ることができます。
(コンテントヘッダを正しく解釈するブラウザ用に、内容をそのままにコンテントヘッダだけ text/plain にしたサンプル)


■余談

JavaScript を使う方法は、あくまでも「今ある技術」を使って応用的に実現しているだけで、JavaScript で外部ファイルの引数をサポートしているわけではありません。仕組みから考えれば、「外部スクリプトに引数を渡す」でなく、「外部スクリプトが自分で引数を取りに行く」です。なんと能動的なのでしょう。(こういうのも引数と言うのでしょうか)
なので別に execute.js?... でなくて execute.js#... でもできたりします。

また、ちょっと考えればわかりますが、自分で取りに行くからには別に引数を <script> に書く必要もなかったりします。たとえば

<meta name="arguments" content="value">

として、

document.getElementsByName("arguments").item(0).content;

でも "value" を取得できます。(このページにもある <meta name="sheetchange" content="enable"> もこの方法を使っていて、content="disable" にするとスタイルシートのON/OFF切り替えが無効になります)

そもそも location.search にしても、単に「URLにある ? 以降の文字列」でしかありません。なくても location.href.match(/\?[^#]+/); で簡単に取得できますし。
Perlのように自動で配列に分割してくれるような、言語レベルのサポートはないです。


■ このページについて

このページは、質疑応答掲示板で使用した回答の残骸です。検索エンジンに捕まったりリサイクルしたりすることもあろうので、なんとなく残してあります。広く公開しているページではありませんが、第3者の閲覧を禁止するものでもありません。
恒久的なURIは保証できないものの、よほどのことがない限り削除されることはないでしょう。心配性な人はページの保存をおすすめします。(IEは mht 形式でないと保存できないかも)