おやまのエンジニアリングブログ

某ゲーム開発会社のフルスタックエンジニアしてます

Javascriptのイベントハンドラーについて調べてみた

ユーザーがクリックした時に最初に実行されるイベントはどれだろう?という疑問が浮かんだので色々調べてみました。MDN(Event - Web API interfaces | MDN)やここ(Javascript - Event order)によるとどうやらイベントの実行順番は自分でコントロールできるようです。調べてわかったことをまとめてみました。

イベントの定義方法

DOM要素のインスタンス関数である、addEventListenerを使って行います。
引数は以下の通り

  • イベントタイプ(必須)
  • 実行する関数(必須)
  • キャプチャを使うフラグ(デフォルト: false)

例)document.body.addEventListener("onClick", function() { alert(1); });

イベントモデルの種類

W3Cのイベントモデルには最も上の要素から最も奥の要素まで順番に実行されるのをイベントキャプチャリング(event capturing)と、最も奥の階層にある要素から最も上の要素まで順番に実行される方をイベントバブリング(event bubbling)の2種類があって図にすると以下のような感じになります。

f:id:toyama4649:20131112021420p:plain

2つの種類の実行順番はキャプチャリング→バプリングとなっています。
なので、例えば以下のようにイベントを登録したとします。

要素1.addEventListener('onClick', function() { alert("A") }, false);
要素1.addEventListener('onClick', function() { alert("B") }, true);

以下を実行したあとに、ユーザーが要素1をクリックした場合、
まずBと書かれたアラートが出力され、その後にAと書かれたアラートが出力されます。

さらに詳しい情報

もっと詳しく知りたい人は以下にさらに詳しく書かれているのでどうぞ。
EventTarget.addEventListener - Web API リファレンス | MDN

jQueryで通信が失敗した時にリトライする方法

$.ajaxを使う際に、通信が切断された時のハンドリングによく困ります。
なので切断された場合、私はユーザーにダイアログを出して再送信を行ってもらうようにしています。

あまり知られていませんが、失敗した時のリクエストと全く同じ内容で再送信することが可能なのです。実は、success/error/complete等のコールバック関数のコンテキストは全て、jQueryの設定パラメータになっているのです。なので、$.ajax(this);とするだけで、再送が可能になるのです。

実際のコードは以下のとおり。

$.ajax({
  url: "/test",
  error: function(xhr) {
    // 通信がタイムアウト
    if(xhr.status === 0) {
      $.ajax(this); // ← これ
    }
  }
});

ちな、githubにこれできるプラグインあったので、リンク貼っておきます。
alexmackey/retryajax · GitHub

HTTPステータスコードを使ってAJAX通信を整理する

クライアントサイドでAJAX通信をハンドリングするときに
HTTPのステータスコードを使うと可読性がアップする上にサーバーもクライアントも
整理できるので非常に便利であることが最近わかったので
ここに使い方をここにまとめておきます。

実装例は以下のとおり。

var $myAjax = function(settings) {
  // ユーザーの定義したエラーハンドラーをキャッシュする。
  var customErrorHandler = settings.error || function() {};

  settings.error = function(xhr, text, error) {
    // ユーザー定義のハンドラーを実行してfalseが帰ってきたら終了する。
    if(customErrorHandler(xhr, text, error) === false) {
      return;
    }

    if(xhr.status >= 500) {
      // ...
    }
  };
  $.ajax(settings);
};


私は以下のエラーに対してハンドリングを入れています。

401: Unauthroized (許可されていない)
セッションが無かった場合に使う。セッションが切れていた場合は、440を使う。

403: Forbidden (禁断)
ユーザーの権限ではアクセス出来ない場所にアクセスしようとした場合に返す。

404: Not Found (見つからない)
ページが見つからなかった時にサーバーが勝手に返す。

409: Conflict (衝突)
サーバー皮の状態とリクエストの状態が一致しない時に返す。

410: Gone (無い)
リソースは無くなっていて、クライアント側でもリソースを破棄すべき時に返す。

440: Login Expired (ログイン期限切れ)
セッションの有効期限が切れた時に使う。

0: Timeout (通信タイムアウト)
これだけは特殊で、サーバーからではなく、ブラウザが返すエラー。
通信が切断されていたり、時間以内に通信が帰ってこなかった場合に発生する。

>=500: サーバーエラー系
いわゆるサーバーエラー系全て。

例外

205: Reset Content (コンテンツをリセット)
ページのリソースが古い場合にリセットするように支持する時に返す。
ただし、ステータスコードが200系なので、successの方に仕込む必要がある。

「後で直す」を援助する shame.css

後で時間があったら戻って治す。そう自分に言い聞かせて、適当なコードを書いてしまう。でも、いつの間にか忘れてしまったり、他人が編集→影響範囲が不明確に→修正不可なんてことになってしまっていることが殆どではないでしょうか。

これが長期に渡って繰り返されると コードが !importantだらけになり、上書きセレクターが大量に定義されます。まさにカオスですね。


これを解決することはできないかと色々調べていたら、shame.css という良さげなものを見つけました。これは、ツールCSSのルールセットではなく、「後で直すコード」は全部まとめて、shame.cssという別ファイルに入れてしまえという思想らしいです。

この手法を適応すると以下の様な良い事があるらしい…

  • メインのCSSをキレイにしておくことができる
  • コードが最適ではない事を明確にできる
  • 他人が修正しやすい
  • git blame shame.css

また適応するにあたって厳守するルールがあるらしい。それは以下の通り…

  • ハックだったらshame.css に書く
  • ハックは全てドキュメントする。必須項目は以下の通り。
    • どこで使われているのか書く
    • なぜ必要なのか
    • どうしてこれで治るのか
    • 時間があったらどう治したいのか
  • shame.cssを定期的に掃除する

!important大量に入っているCSS読むのはもうウンザリなので、とりあえず次のプロジェクトではshame.cssを実装してみようと思っています。

 

ソース
shame.css - http://csswizardry.com/2013/04/shame-css/