pirosikick's diary

君のハートにunshift

パーフェクトJavaScript

パーフェクトJavaScript (PERFECT SERIES 4)

パーフェクトJavaScript (PERFECT SERIES 4)

感想

Javascriptは割りと好きで勉強してたし、詳しい方だと思ったけど意外と知らないことが多かった。あとこの本は網羅している深さと広さがちょうどよい気がする。html5とかnodeとか要点がうまくまとまっていてリファレンスではないけど会社の机に常に置いておきたいなーと思った。

知らなかったこと、Tips

イベントハンドラとイベントリスナの違い

恥ずかしながら全く同じものと思っていたし、どっちがどっちか区別できていなかった。。

var btn = document.getElementById("test");

// イベントハンドラ
btn.onclick = function (e) { ・・・ }

// イベントリスナ
btn.addEventListener("click", function (e) { ・・・ }, false);

イベントハンドラもイベントリスナもコールバックを1つしか設定できないと思っていたけど、イベントリスナに関してはイベントターゲット、イベントタイプ、フェーズ(第3引数)の組み合わせが同じでなければ同じターゲットに複数コールバックを指定できる。

function sayFoo() {
    alert('foo');
}
function sayBar() {
    alert('bar');
}
function sayBaz() {
    alert('baz');
}

// イベントハンドラの場合は1つのみ
btn.onclick = sayFoo;
btn.onclick = sayBar; // ← こいつで上書きされる 

// イベントリスナ
btn.addEventListner("click", sayFoo, false);
btn.addEventListner("click", sayBar, false);
btn.addEventListner("click", sayBaz, false);
btn.addEventListner("click", sayFoo, false); // イベントターゲット、イベントタイプ、フェーズが同じなので無視される
stopPropagationとstopImmediatePropagationの違い

どっちも「イベントの伝播をストップする」だけど、stopPropagationは次のターゲットへの伝播を停止して、stopImmediatePropagationは同じターゲットに設定されている他のイベントリスナへの伝播も停止するという違い。例えば下記のようなhtmlがあったとして、

  <div id="wrapper">
    <div id="inner" style="width:100px;height:100px;border:1px solid black;">
      Click me!!
    </div>
  </div>

下記のようなjavascriptがあった場合、

    window.onload = function () {
      var wrapper = document.getElementById("wrapper"),
            inner     = document.getElementById("inner");

      wrapper.addEventListener("click", function (e) {
        alert("wrapper!!");
      });
      inner.addEventListener("click", function (e) {
        alert("inner 1!!");
      });
      inner.addEventListener("click", function (e) {
        alert("inner 2!");
      });
    }

「inner 1!!」「inner 2!!」「wrapper!!」の順でアラートが出る。
でそれを下記のようにinnerへのリスナの片方でstopPropagationを実行すると、「inner 1!!」「inner 2!!」とアラートが出て「wrapper!!」は出なくなる。

    window.onload = function () {
      var wrapper = document.getElementById("wrapper"),
            inner     = document.getElementById("inner");

      wrapper.addEventListener("click", function (e) {
        alert("wrapper!!"); // 伝播されなくなるので実行されない
      });
      inner.addEventListener("click", function (e) {
        alert("inner 1!!");
        e.stopPropagation(); // 追加
      });
      inner.addEventListener("click", function (e) {
        alert("inner 2!");
      });
    }

さらに下記のようにstopImmediatePropagationに変更すると、「inner 1!!」だけしか出なくなる。

    window.onload = function () {
      var wrapper = document.getElementById("wrapper"),
            inner     = document.getElementById("inner");

      wrapper.addEventListener("click", function (e) {
        alert("wrapper!!"); // 伝播されなくなるので実行されない
      });
      inner.addEventListener("click", function (e) {
        alert("inner 1!!");
        e.stopImmediatePropagation(); // 追加
      });
      inner.addEventListener("click", function (e) {
        alert("inner 2!"); // 伝播されなくなるので実行されない
      });
    }
フォームを使ってページ遷移を発生させない方法

form要素にtargetプロパティというのがあって、そこに同ページ内にあるiframeを指定することでsubmitしてもページ遷移が発生しない。

<html>
<body>

<!-- resultにiframeのnameを指定 -->
<form method="post" action="post.php" target="result>
  <input type="text" name="text" />
  <input type="submit" />
</form>

<!-- 送信用のiframe -->
<iframe name="result" style="width:0;height:0;border:none;"></iframe>

</body>
</html>
localStorageをローカル変数と同期させる

下記のようにすると、管理が楽になり、頻繁にlocalStorageにアクセスしないのでパフォーマンスもよい。

var NAME_SPACE = "NAME_SPACE",
    storage = null;

// onload時にlocalStorage → ローカル変数
widow.onload = function () {
    try {
        storage = JSON.parse(localStorage[NAME_SPACE]  || "{}");
    } catch (e) {
        storage = {};
    }
};

// beforeunload時にローカル変数 → localStorage
window.onbeforeunload = function () {
    localStorage[NAME_SPACE] = JSON.stringify(storage);
}

次はこれ

リファクタリング・ウェットウェア ―達人プログラマーの思考法と学習法

リファクタリング・ウェットウェア ―達人プログラマーの思考法と学習法

もう2章くらい読んだけど超おもしろい!