Webプログラミング: 短期集中コース

あなたはおそらくこれをウェブブラウザで読んでいるだろうし、ワールドワイドウェブについてもちょっとは親しんでいるだろう。この章には、ウェブを動かすための様々な要素と、それらをJavaScriptに結びつける手段の、手早く、表面的な紹介が含まれる。この3章後ではもっと実用的な、JavaScriptでウェブページを点検し、変更するいくつかの手段を見せよう。


インターネットは、基本的には、ただ世界中に広がったコンピューター・ネットワークである。コンピューター・ネットワークはコンピューター同士でのメッセージのやりとりを可能にする。ネットワークの下に横たわるテクニックは面白い課題だが、この本の課題では無い。知らなければならないのは、典型的な、サーバーと呼ばれる1台のコンピュータが他のコンピューターから話しかけられるのを待っているということだ。1度他のコンピューター、クライアント、がこのサーバーとコミュニケーションを開いたら、特定の言葉、プロトコルを使って交換すべきものを交換するだろう。

インターネットは、多くの異なるプロトコルのメッセージを運ぶために使われる。チャットのためのプロトコル、ファイル共有のためのプロトコル、悪意をもったソフトウェアが、それをインストールされた間抜けなコンピュータを制御するために使われるプロトコル、その他。もっとも我々の興味を引くプロトコルはワールドワイドウェブに使われるものだ。それはHTTPと呼ばれ、Hyper Text Transfer Protocolを意味し、ウェブページとそれらに結びついたファイルを取り出すのに使われる。

HTTPのコミュニケーションにおいて、サーバーはウェブページが格納されたコンピューターである。クライアントは、あなたが持っているもののように、サーバーにページを要求し、表示するコンピューターだ。このようなページの要求は’HTTPリクエスト’と呼ばれる。


インターネットを通じてアクセスできるウェブページと他のファイルはURLによって識別され、これはUniveral Resource Locatorsの略である。URLはこのようなものだ。:

http://acc6.its.brooklyn.cuny.edu/~phalsall/texts/taote-v3.html

これは3つの部分により作られている。最初は、http://、このURLがHTTPプロトコルを使うことを示す。他にもFTP(File Transfer Protocol)といったようなプロトコルがあり、それらもURLを使う。次の部分、acc6.its.brooklyn.cuny.eduはこのページが見つかるであろうサーバーの名前だ。URLの最後の、/~phalsal/texts/taote-v3.htmlはこのサーバー上の個別のファイルの名前だ。

長い間、ワールドワイドウェブはブラウザーを使ってアクセスされてきた。URLをタイプするかリンクをクリックされたら、ブラウザは妥当なHTTPリクエストを妥当なサーバーに送る。もし全てがうまくいったら、サーバーはブラウザにファイルを送り返すことで応答し、ブラウザは1つまたは他の手段でユーザーにそれを見せる。

この例の場合、取り出されたファイルはHTMLドキュメントであり、ウェブページとして表示される。6章でHTMLについて少々論じ、イメージファイルを参照できることを知った。9章ではHTMLページは、JavaScriptコードのファイルをロードするために<script>タグを含むことを知った。HTML文書を見せるとき、ブラウザはサーバーからこれらの拡張ファイルを取り出し、文書に追加することができる。


例えURLが1つのファイルを指していることになっていても、ウェブサーバーにはファイルを見つけてそれをクライアントに送るより複雑なことをすることが可能である。 – 1つには、このファイルを何らかの手段で処理できるし、あるいは結局ファイルは無くて、URLを与えられ、それに関する文書を何らかの方法で生成するプログラムだけがあるのかもしれない。

サーバー上の文書を変形または生成するプログラムはウェブページを静的でないものにする人気のある手段だ。ファイルがただのファイルであればそれは常に同じものであるが、リクエストの度にそれを毎回組み立てるプログラムがあれば、例えば、その人がログインしているかどうか、または決まった設定を指定しているかどうかといったことで、それぞれの人に異なる見栄えを作ることができる。これでウェブページの内容の管理が簡単になる ― ウェブサイトに新しいものを追加する度に新しいHTMLファイルを追加するよりも。新しい文書は中央の記憶装置に追加され、プログラムはそれがどこに見つかるか、クライアントにどう見せるか知っているのだ。

この種類のウェブプログラミングはサーバーサイドプログラミングと呼ばれる。これは文書がユーザーに送られる前に作用する。ある場合には、ページが送られた、ユーザーがそれを見る前にプログラムを動かした方が実用的である。クライアントコンピューター上でプログラムが動くため、こちらはクライアントサイドプログラミングと呼ばれる。クライアントサイドウェブプログラミングのためにJavaScriptは発明された。


クライアントサイドでプログラムを動かすには固有の問題がある。訪れているページの上でどんな種類のプログラムが動くかを本当に前もって知ることはできないだろう。それはあなたのコンピュータの情報を他に送るかもしれず、何かにダメージを与えるかもしれず、システムに侵入するかもしれず、ウェブをサーフィンすることはむしろ危険な行動になった。

このジレンマを解決するため、ブラウザはJavaScriptが行えることを厳しく制限している。あなたのファイルを読んだり、そのウェブページに関係しない何物をも変更することはできない。このようにプログラミング環境を分離することはサンドボクシングと呼ばれる。プログラムが有益なことをするのに必要な場所を与え、かつ同時にそれらが何かを傷つけることを防ぐのは簡単なことでは無い。数ヶ月毎に、JavaScriptプログラマーは制限を回避する新しい方法を発見して何かを傷つけたりプライバシーを侵害したりする。ブラウザに責任を負っている人々は、このトリックを不可能にするため、それらのプログラムを変更し、すべては再び良くなる – 次の問題が発見されるまでは。


最初に広く使われるようになったJavaScriptのトリックの1つはwindowオブジェクトのopenメソッドだ。URLを引数として取り、新しいウインドウを開いてそのURLを見せる。

var perry = window.open("http://www.pbfcomics.com");

6章でポップアップブロックをオフにしていれば、この新しいウインドウがブロックされるチャンスがある。ポップアップブロッカーが存在すべき理由があるのだ。ウェブプログラマー、特に、広告に人々の注意を払わせようとする者達は、今のような貧弱なwindow.openメソッドを悪用し、多くの人々に激怒される。とは言いつつ、この本でも例のページを見せるのにそれを使っている。一般的なルールとしては、スクリプトはユーザーに確認することなしに新しいウインドウを開くべきではないということだ。

注意して欲しいのは、opensetTimeoutとその仲間)はwindowオブジェクトのメソッドであるため、window.の部分は切り離せないということだ。関数が’通常通り’に呼び出された場合、トップレベルのメソッドとして呼び出され、ウィンドウもそうなる。個人的には、openは少々一般的すぎるように思える。私は通常、window.openとタイプしており、それはウインドウが開かれるということを明確にするためだ。

window.openの戻り値は新しいウインドウである。これはそのウインドウでスクリプトが走っている間のグローバルなオブジェクトであり、ObjectのコンストラクタやMathオブジェクトのような、全ての標準的なものを含んでいる。しかしそれらを見ようとしても、多くのブラウザは(おそらく)そうさせないだろう…。

show(perry.Math);

これは先ほど述べたサンドボクシングの一部である。ブラウザで開かれたページは、例えばログインしたサイトのような、あなたにだけ意味のある情報を見せ、もし無作為のスクリプトが実行されてそれを読んだりできたとしたらそれはまずいことだ。このルールの例外は、同じドメインで開かれたページだ:eloquentjavascript.netのページでスクリプトが実行されているとき、開かれた同じドメインの他のページも、このページに望む全てのことができる。

開かれたウインドウはcloseメソッドで閉じることができる。もしあなたが自分で既に閉じていなければ…

perry.close();

他のサブ文書としては、フレーム(文書の中の文書)のようなものがあり、これもJavaScriptプログラムからはウインドウのように見え、それ自身のJavaScript環境を持つ。実際、コンソールとしてアクセスしている環境はこのページの小さな隠されたフレームとして存在し – この方法で、あなたがページ全体をうっかり壊してしまわないようにしている。


全てのwindowオブジェクトはdocumentプロパティを持ち、それは、そのウインドウに表示されている文書を表現するオブジェクトを含んでいる。このオブジェクトは、例えば、locationプロパティ、文書のURLに関する情報を含んでいる。

show(document.location.href);

document.location.hrefに新しいURLをセットするとブラウザに他の文書がロードされる。documentオブジェクトの他の利用としてはそのwriteメソッドがある。このメソッドは、文字列の引数を与えられたとき、文書にHTMLとして書き出す。完全にロードされた文書に対して使われたとき、それを望んでいなくても、通常は与えられたHTMLで文書全体が置き換えられる。このアイデアは、文書がロードされている間にスクリプトの呼び出しを行い、そのスクリプトにより書き出されたHTMLを文書のscriptタグのあった場所に挿入するためにある。これはページに動的に要素を追加する単純な手段である。例えば、現在の時刻を表示するちょっとした文書をここに示す。

print(timeWriter);
var time = viewHTML(timeWriter);
time.close();

12章では、より明瞭で融通の利く、文書を更新するためのテクニックを見せるが、しかし、しばしば、document.writeは最も良く、最も単純な手段である。


ウェブでのJavaScriptの利用としてもう一つ人気の高いのはフォームの処理だ。この場合の’フォーム’の役割を完全には分ってないだろうから、ここで手っ取り早く要約しよう。

基本的なHTTPリクエストはファイルの単純なリクエストだ。このファイルが本当は受動的なファイルではなく、サーバー側のプログラムであるとき、リクエストでファイル名よりも有益な情報を含めるようにすることができる。この目的のため、HTTPリクエストには’パラメータ’を追加することができるようになっている。例を示そう。:

http://www.google.com/search?q=aztec%20empire

ファイル名(/search)の後に、URLは疑問符で続けられており、その後にパラメータが続いている。このリクエストはパラメータは1つ持っており、q(たぶん’query’だろう)と呼ばれていて、その値はaztec empireである。%20の部分は空白に一致する。空白、アンパサンド、疑問符のような、URL中にあってはならない値の文字コードである。これらは%とその文字コードの値[^1]で置換されることによりエスケープされ、それはサーバーが文字列や正規表現の中で、読みにくくなるにもかかわらず、バックスラッシュを使うのと同じ目的である。

[^1] 文字の値はASCII標準によって決まり、0から127までの数値にアルファベットの文字と記号の集合が割り当てられている。この標準は2章で触れたUnicode標準の先駆けである。

JavaScriptはこれらのコードを文字列に追加し、あるいは取り除くためにencodeURIComponentdecodeURIComponent関数を提供している。

var encoded = encodeURIComponent("aztec empire");
show(encoded);
show(decodeURIComponent(encoded));

リクエストに1つより多いパラメータが含まれるとき、それらはアンパサンドで分割される、このように…

http://www.google.com/search?q=aztec%20empire&lang=nl

フォームは、基本的には、ブラウザのユーザーにこのようなパラメータ付きのURLを作らせる簡単な手段だ。テキストのためのインプットボックスや、チェックされたか否かのチェックボックス、値のセットから選択するためのアレなど、いくつものフィールドを含む。通常は、’サブミット’ボタンと、ユーザーには見えない、情報を送る’アクション’のURLも含まれている。サブミットボタンがクリックされるか、エンターが押されるかしたら、フィールドに入力された情報は、このアクションのURLにパラメータとして追加され、ブラウザはこのURLをリクエストする。

ここに単純なフォームを含むHTMLがある。:

<form name="userinfo" method="get" action="info.html">
  <p>Please give us your information, so that we can send
  you spam.</p>
  <p>Name: <input type="text" name="name"/></p>
  <p>E-Mail: <input type="text" name="email"/></p>
  <p>Sex: <select name="sex">
            <option>Male</option>
            <option>Female</option>
            <option>Other</option>
          </select></p>
  <p><input name="send" type="submit" value="Send!"/></p>
</form>

見て分るとおり、フォームの名前でJavaScriptからアクセスすることができる。フィールドの名前でそれらを格納するHTTPパラメータの名前が決まる。このフォームを送ることで、このようなURLが作られる。:

http://planetspam.com/info.html?name=Ted&email=ted@zork.com&sex=Male

他にもいくつかのタグやプロパティをフォームで使えるが、JavaScriptに集中できるよう、この本では単純なものに留めておこう。


上記の例のフォームのmethod="get"プロパティはこのフォームが値を上で見たようなURLのパラメータにエンコードすることを示す。postという、パラメータを送る代わりのメソッドもある。postメソッドを含むHTTPリクエストは、URLに加えて、データのブロックを含む。postメソッドを使うフォームはURLの代わりにこのデータブロックにパラメータの値を入れる。

getメソッドで結果のURLが長くなりすぎるような大きなデータの塊を送るときに、postは通常、より便利である。しかしこの2つのメソッドの違いはただ利便性の問題のみではない。伝統的に、getリクエストはサーバーから文書を求めるためのリクエストであり、postはサーバー上で何かを変更するためのアクションを起こすリクエストである。例えば、インターネットのフォーラムの過去のメッセージのをリストを得るのはgetリクエストで、新しいメッセージを追加するのはpostリクエストである。この区別に紙面を割く理由がある – サーチエンジンで使われるような、ウェブを自動的に探索するプログラムは、一般的にはgetリクエストのみを使うだろう。もしgetリクエストでサイトを更新できたら、それらの’クローラ’はあらゆる種類のダメージを与えることを行うかもしれない。


ブラウザがフォームを含むページを表示しているとき、JavaScriptはフォームのフィールドに入力された値を監視したり変更したりできる。これが、サーバーに送られる前の値をチェックしたり、決まったフィールドを自動的に埋めたりといった、あらゆる種類のトリックを可能にする。

上記で見たフォームをexample_getinfo.htmlというファイルに入れた。開いてみよう。

var form = window.open("example_getinfo.html");

サーバー名が含まれないURLは、相対URLと呼ばれる。相対URLはブラウザによって、現在の文書と同じサーバーのファイルを参照しているものと解釈される。スラッシュで始まらない限り、現在の文書のパス(またはディレクトリ)も引き継がれ、与えられたパスがそれに追加される。

nameフィールドに空のものが残っておらず、e-mailフィールドが正しい電子メールアドレスのようなものを含むようになったときのみサブミットできるように、フォームに正当性チェックを追加しよう。明示的に’Send!’ボタンを押すまでフォームをサブミットされたくないから、そのtypeプロパティを"submit"から何の効果も無いただのボタンである"button"に変更しておこう。 – 13章でこれを行うもっと良い方法を見せるが、今は、ネイティブなメソッドを使おう。


新しく開いたウィンドウ(もし閉じてしまっていたら、まずそれを再度開こう)を働かせるには、このようにコンソールにそれを’attach’させよう。:

attach(form);

このようにした後は、コンソールで実行されたコードは与えられたウインドウで走る。正しいウインドウで動いていることは、文書のlocationtitleプロパティで確認できる。

print(document.location.href);
print(document.title);

新しい環境に入ったため、formのような、今まで定義した変数はもう存在していない。

show(form);

最初の環境に戻るには、detach関数(引数無し)を使う。しかし、まずは、フォームに正当性システムを追加しなければならない。


文書の中の全てのHTMLタグはJavaScriptオブジェクトと関連づけられる。これらのオブジェクトは文書のほとんど全ての外観を監視し操作するのに使える。この章ではフォームとフォームフィールドのオブジェクトを扱うが、12章ではこれらのオブジェクトの詳細について語る。

documentオブジェクトはformsという名のプロパティを持ち、文書の全てのフォームへの名前によるリンクを含む。我々のフォームはname="userinfo"というプロパティを持っているので、userinfoプロパティの下に見つかる。

var userForm = document.forms.userinfo;
print(userForm.method);
print(userForm.action);

この場合、HTMLのfromタグで与えられるmthodactionプロパティはJavaScriptオブジェクトのプロパティとしても表現される。このような場合はしばしばあるが、常にでは無い:いくつかのHTMLプロパティはJavaScriptでは違うスペルであり、他は全く存在していない。12章で全てのプロパティを得る手段を見せよう。

formタグのオブジェクトはelementsプロパティを持ち、それはformのフィールドに含むオブジェクトを名前で参照する。

var nameField = userForm.elements.name;
nameField.value = "Eugène";

テキストインプットオブジェクトはvalueプロパティを持ち、それはその内容を読んだり変更したりするのに使われる。上記のコードの実行後、フォームウインドウを見れば、埋められた名前を見ることができるだろう。


[演習 11.1]

フォームフィールドの値を読めるようになれば、validInfo関数を書くことができるようになる。引数としてフォームオブジェクトを取り、真偽値の値を返す:nameフィールドに空のものが無く、emailフィールドに電子メールアドレスのようなものが含まれていればtrue、そうでなければfalseとなる。この関数を書け。

[解答を見る]

function validInfo(form) {
  return form.elements.name.value != "" &&
    /^.+@.+\.\w{2,3}$/.test(form.elements.email.value);
}

show(validInfo(document.forms.userinfo));

電子メールアドレスのチェックには正規表現を使うことを考えたよね?


やるべきことのうち、残っているのは、人が’Send!’ボタンをクリックしたときの挙動だ。あっという間で、何もすることは無い。onclickプロパティを設定するだけだ。

userForm.elements.send.onclick = function() {
  alert("Click.");
};

ちょうどsetIntervalsetTimeoutに与えられたアクション(8章のように、onclick(または同様の)プロパティに、関数やJavaScriptコードの文字列を値として格納できる。この場合、警告ウインドウを開く関数を与える。クリックしてみよう。


[演習 11.2]

ボタンのonclickプロパティに新しい値 ― フォームをチェックする関数を与えてフォームのバリデーターを完成させよ。正しければサブミットし、またはそうでなければ警告のメッセージをポップアップする。formオブジェクトが、パラメータを持たず、フォームをサブミットする、submitメソッドを持っていることを知っていると役立つだろう。

[解答を見る]

userForm.elements.send.onclick = function() {
  if (validInfo(userForm))
    userForm.submit();
  else
    alert("Give us a name and a valid e-mail address!");
};

フォームのインプットだけでなく、ボタンやリンクのような’選択可能なもの’に関するもう一つのトリックとして、focusメソッドがある。ユーザーがページに入ったとき、決まったフィールドからタイプを始めたいだろうことが確実に分っているとき、彼にクリックや他の手段で選択させるのではなく、スクリプトでカーソルをそこに移動させることができる。

userForm.elements.name.focus();

フォームが他のウインドウにあるため、何かが選択されたかは明らかでなく、使われているブラウザに依存する。あるページはそのフィールドが完全に埋まったように見えるとき、自動的にカーソルを次のフィールドに移動させる – 例えば、郵便番号をタイプするときだ。これはやり過ぎてはいけない – これはページをユーザーの期待しないように振る舞わせる。もし手動でカーソルを移動しようとタブを押したり、最後の文字をミスタイプして削除したいと思っていたとしたら、このような手品めいたカーソル移動はとても邪魔だ。


detach();

バリデーターをテストしよう。正しい情報を入力してボタンをクリックしたとき、フォームはサブミットされるだろう。もしコンソールがまだ接続されていたら、ページがリロードされてJavaScript環境が新しいものに置き換わるため、デタッチされる。

もしフォームウインドウをまだ閉じていなければ、これで閉じよう。

form.close();

上記は簡単なように見えるだろうが、私は保証しよう、クライアントサイド・ウェブ・プログラミングは公園の散歩ではないと。それは、時には、とても痛い体験となる。なぜか?なぜなら、クライアント・コンピュータ上で実行されることを想定されたプログラムは、一般的に、全ての有名なブラウザで動かねばならないからだ。これらのブラウザのそれぞれがちょっとずつ異なるように動く。悪いことには、これらそれぞれが固有の問題のセットを抱えている。プログラムが、数百万ドルの会社によって作られているからというだけで、バグと無縁であると想定しないこと。我々、ウェブ・プログラマーにとって、プログラムを厳格にテストすることが、間違いを防止することで、それを動かす道を見つけることだ。

「私は見つけた問題やバグをブラウザの作成者にただ報告するだけにしよう、そうすれば直ちに彼らがそれを確かに解決してくれるだろう。」そう考える者もいるかもしれない。これらの人々は大きく落胆することになる。InternetExplorerの過去のバージョンの多くは、未だウェブ・サーファーの70%に使われている(そして全てのウェブ・ディベロッパーからけなされがちな)ブラウザであるが、5年以上前から知られているバグが未だに残っている。重大なバグさえも。

しかしガッカリしないように。極端に強迫的な種類の考え方の持ち主にとっては、このような問題はすばらしいチャレンジの提供である。そして時間の浪費を好まぬ我々のために、注意深く、そしてブラウザの機能の暗い隅を回避して、一般的にあなたが多すぎるトラブルの中に飛び込まないよう守ってくれる。


バグはおいても、ブラウザ間の、設計によるインターフェースの違いは面白いチャレンジを作る。現在の状況はこのように見える:一方で、全ての’小さい’ブラウザがある:Firefox、Safari、Operaが最も重要なものだが、他にももっとある。これらのブラウザは全て、このようなものの標準インターフェースを定義することにより、ウェブの混乱をより少なくしようとしている組織であるW3Cにより、開発された、または開発中の標準に忠実であるために合理的に努力している。もう一方で、MicrosoftのブラウザであるInternetExplorerがあり、これらの標準がまだ存在しなかったころの、一時は優位に立っていて、他の人々が従っている標準に合わせるための努力を行っていない。

ある領域、HTML文書の内容をJavaScriptからアプローチできるようにする方法(12章)のようなところでは、標準はInternetExplorerが発明した方法をもとにしていて、全てのブラウザで多かれ少なかれ同じように動く。他の領域では、イベント(マウスクリック、キー押下など)のハンドリング(13章)のようなところでは、InternetExplorerは他のブラウザと激しく異なる。

長い間、部分的には、平均的なJavaScript開発者の無知により、そして部分的にはブラウザの非互換性という事実により、InternetExplorerのバージョン4、5と古いバージョンのNetscapeのようなブラウザがまだ一般的であった時はもっと酷く、ユーザーが動かしているブラウザを判定して、そのような相違点を扱うのが普通のやり方だった。そして、それぞれのブラウザ向けに代替の解決を行うためにコードが散乱した – もしInternetExplorerならこうする、Netscapeならああする、考慮していない、他のブラウザであれば、それがベストであることを望むだけ。いかに恐ろしく、混乱した、そして長いプログラムであるかが想像できるだろう。

多くのサイトもまた、ブラウザで開かれたときに’サポートされてません’と、ロードすることをただ拒まれる。これはいくつかのマイナーなブラウザに、そのプライドを飲み込み、ただそのようなページをロードできるようにするために、InternetExplorerであるかのように見せかけさせた。navigatorオブジェクトのプロパティは、ページを読み込んだブラウザについての情報を含むが、しかしこの情報にウソがあるため、とても信頼できるものではない。あなたのブラウザが表示するものを見てみよう。[^2]:

[^2] あるブラウザはnavigatorオブジェクトのプロパティを隠しているようで、この場合は何もプリントされない。

forEachIn(navigator, function(name, value) {
  print(name, " = ", value);
});

より良いアプローチは試してみてることと、ブラウザの違いからプログラムを’隔離する’ことだ。もし必要なら、例えば、送信ボタンのonclickプロパティを設定してクリックをハンドルするような、イベントについて、もっと探し出したいなら、eventというInternetExplorerのトップレベルのオブジェクトを見なければならない、しかし他のブラウザでは最初の引数はイベントハンドリングの関数の受け渡しに使わなければならない。これと、イベントに関する違いの数々をハンドリングするための、イベントと物を結びつける、全ての配管の面倒を見てイベントハンドリング関数それ自体を全てのブラウザで同じ物にするヘルパー関数を書けるだろう。13章でそのような関数を書こう。

注: ブラウザのごまかしについての以降の章での言及は2007年初頭での状況に基づいていて、あるポイントについてはもう正しくないかもしれない。


これらの章はブラウザのインターフェースの課題についての表面的な紹介のようなものだけを与える。それらはこの本の主題ではなく、そしてこの薄い本自体に書くには複雑すぎる。これらのインターフェースの基本を理解したら(そしてHTMLについて理解したら)、オンラインの個々の情報を探すのは難しくないだろう。FirefoxInternetExplorerのインターフェースの文書が手始めには良い。

次の章の情報は’前世代の’ブラウザのごまかしを扱わない。InternetExplorer 6、Firefox 1.5、Opera 9、Safari 3、またはそれらのブラウザのそれ以降のバージョンを扱う。その多くはおそらく現代的だがKonquerorのようなよく分らないブラウザもあり、これは広範囲にはチェックされていない。幸運にも、これらの前世代のブラウザはほとんど死滅していて、もう余り使われることはない。

しかしながら、JavaScriptのないウェブユーザーのグループがまだ存在する。このグループの大部分は正規のグラフィカルなブラウザを使うが、セキュリティ上の理由からJavaScriptをオフにしている人々だ。それからテキストのブラウザを使っている人々もいるし、目の見えない人々のためのブラウザもある。’真面目な’サイトで働くなら、プレーンなHTMLシステムで動くものから始めて、それからJavaScriptで本質的でないトリックと利便性を追加するのが良いアイデアだ。