Yokohamaのウェブサイト。連続の機能強化でついに「いいね機能」と「コメント機能」が追加された。アプリケーションを追加する条件が整っていたのだ。そのためのテストもしてあった。そのため、実は今回コード追加はわずかで、両方合わせても23行ほどにとどまる。簡単だが、確証が持てないためにテストと本番環境のための修正を行ったりして結構な時間がかかった。これに関してはみるべきところはあまりない。受け取ったパラメータをファイルに書き込めば良いだけだからだ。ちなみに、連打しやすいアプリケーションを入れるために連打の対策もサーバーにしてあった。実は先のパフォーマンスチューニングはこの対策によってパフォーマンスが低下してしまったため、これをカバーするついでに行ったものだった。いいね機能の設計いいね機能はごくシンプルだ。Pandocテンプレートを使ってページタイトルを埋め込むことができるので、これだけ使うのであれば単にテンプレートの中にフォームを書けばいいだけ、ということになる。IPアドレス(Rack::Request#ip)も特定に使用している。ポイントは「一度送信したら表示を変更、送信を無効化する」ということだ。inputタグのdisabled属性を使うことで送信ボタンを無効化している。/></form>そう、この機能はHTML上でインラインで書かれているのだ。このような書き方はW3C的には推奨されないのだが、Googleは推奨している。実際、これだけのためにJavaScriptファイルをロードさせることをしたくなかったので、インラインにした。DOMにおいてフォーム部品は連想配列のようにアクセスできるようになっているsubmitボタンのラベルはvalueであるdisabledによってフォーム部品を無効化できるである。これらの処理は送信の「前に」行われる。これは正しいことではないが、問題はない。送信できなかったからといってユーザーが修正するなどの手を入れる余地はない特に返信を必要とするものでもないので、送信失敗はクリティカルな問題でもないサーバーエラーなどは表示されるようになっているからだ。実際は現代のブラウザは基本的に2XXステータスで空コンテンツを返すとページ変遷しないようになっている。Content)が用意されているため、204を返す仕様だ。できるだけブラウザの標準機能に頼るようにしている。公開されるものではなく、sanitizeしなくても問題が発生しない設計になっている(単に文字列として扱う以上の取り扱いがなされる条件で使用しない)ため、非常に楽だ。難しく考えるよりも、シンプルで挙動をちゃんと把握できていて、余計なことをしない方法をとるのが最も楽に、確実に、バグなく設計できる。この機能に関しては「コメントフォームの表示」などが必要になり、JavaScriptが必要になった。コメントするというあまりとらない行為のための部品であり、常にロードすることは非常に好ましくない。そこでとった方法は「JavaScriptの遅延ロード」であり、「クリック時にelementを追加してJavaScriptを読み込む」という方法だ。type="button">コメントを残す</button>これでscript要素を追加している。呼び出されたあとどうしているのか...というと、こんな感じだ。}})()そう、コメントフォームは標準でHTMLに含まれていない。「フォーム部分はJavaScriptがロードされたときにinnerHTMLで書く、という方法をとっている。「クリックされたときにはじめて必要とされるのでドキュメントノードを追加してロードする」という手法は稀に使われるが、さすがにそれでHTMLドキュメントそのものを生成するというのはまず見ない手法になっている。連打されたときのことを考えて、clickイベントを発生する値に特殊なプロパティを埋め込み、何度も実行されないようにしている。これは本当に連打したときだけ機能するもので、JavaScriptシングルスレッドなので一回このスクリプトに入っていまえばこのスクリプト中に割り込まれることはない。連打されるとイベントがキューに入ってしまうので何度も実行されてしまうことから、そうしたことがないようにしている。シングルスレッドなので、このスクリプトが実行されているのにプロパティが設定されないうちにまた実行されるということはない。元になるフォーム自体は存在していて、submitボタンを配置すればフォームの送信は可能だ。そのため、submitイベントに対するイベントリスナーを設定するものはHTML上に静的に存在している。その上で「フォームはやっぱり閉じられたほうがいいな」ということでボタンクリックに対するイベントの変更、及びボタンラベルの変更を行っている。送信を行った場合はもうコメントフォームは使わないので、コメントフォームは非表示にして(削除はしていない)ラベルを変更している。ラベル変更だが、input部品とは違ってbutton要素は子要素テキストノードがラベルになっているので、dataプロパティの書き換えによって変更している。DOMによってイベントを変更している。いつもaddEventListenerを使っていたので、珍しい。イベントの削除はイベントにnullを代入するだけだ。HTMLで全て含めてしまえばJavaScriptは排除できるのだが、これ以上余計な要素を組み込むことはできれば避けたいためこのような仕様になっている。DOMコンパイルは割と重い。デザインポリシーからいって、多くの場合余計なコメントフォームを常に表示させておくというのは美しくないとも思っている。JavaScriptを使わずCSSでオンオフするようにもできるが、そうすると今度はボタンのテキストを変更するのが難しいし、連打や再送信を防ぐのも難しい。このような付加コンテンツはユーザービリティの点からも素直に使うべきだと思う。こちらもいいね機能同様、送信する前に状態を変更してしまい、成功すれば204を返す仕様。Eventについて意見をもとめられることがたまにあるのだが、私としてはあまり勧められないと考えている。DOMはHTMLの構造に依存する。true"とかやってしまうと、ドキュメントの構造が変わるたびに修正だ。DOMで特定する」のであれば悪くない。Eventはイベントリスナーが1つしか登録できないためモジュール設計になっている場合や、なんらかのライブラリを使っている場合は使ってはいけない。イベントを「追加」する場合はLevel2で、イベントを「設定」する場合はLevel0という使い分けも考えられる。HTMLに直接書く場合はLevel0しか書けないので、その部品の基本的な動作と定義されているならLevel0でもいい。Eventの使いどころは今回のようなものが唯一だと思う。