end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

javascript によるデザインパターン - ステートパターン

http://gihyo.jp/book/2013/978-4-7741-5489-3/support
どーも身についていませんが、gihyo.jpの↑このサンプルは分かりやすい。

ステートパターンなので、例えば、actionクラスでの処理の振分けに適用できます

<html>
<head>
<style>
#list li {
  cursor: pointer;
  font-size: 20px;
}
#drag-item {
  display: none;
  background:yellow;
  alpha: 0.5;
  z-index: 99;
  position: absolute;
  font-size: 20px;
}
.drag-over {
  border-bottom: 2px dashed #777;
  background: #FED;
}
.text-editing {
  z-index: 99;
  position: absolute;
  font-size: 20px;
}
</style>
<script src="jquery-1.3.2.js"></script>
<script>
$(function() {
  var state = null;
  var selected = null;

  // イベント処理を状態オブジェクトに委譲
  $("#list li").mousedown(function(event) {
    state.onMouseDown(event);
  });
  $("#list").mousemove(function(event) { 
    state.onMouseMove(event);
  });
  $("#list").mouseup(function(event) {
    state.onMouseUp(event);
  });
  $("#list").dblclick(function(event) {
    state.onDblClick(event);
  });
  $(document).keydown(function(event) {
    state.onKeyDown(event);
  });

  // 状態遷移関数の定義
  function transitState(newState) {
    $("#debug").text(state + " -> " + newState);
    state = newState;
  }

  // AbstractStateクラスの定義
  var AbstractState = function() {};
  AbstractState.prototype = {
    initialize: function() {},
    onMouseDown: function(event) {},
    onMouseMove: function(event) {}, 
    onMouseUp: function(event) {}, 
    onDblClick: function(event) {}, 
    onKeyDown: function(event) {}, 
    toString: function() {
      return "AbstractState";
    }
  };

  // Normalクラスの定義
  var NormalState = function() {
      selected = null; // 選択解除
  };
  $.extend(NormalState.prototype, AbstractState.prototype, {
    onMouseDown: function(event) {
      var target = event.target;
      if (target.tagName == "LI") {
        // 移動中状態に遷移
        transitState(new DragMovingState(target));
      }
    },
    onDblClick: function(event) {
      var target = event.target;
      if (target.tagName == "LI") {
        // テキスト編集中状態に遷移
        transitState(new TextEditingState(target));
      }
    },
    toString: function() {
      return "NormalState";
    }
  });

  // DragMovingStateの定義
  DragMovingState = function(target) {
    selected = target;
    $("#drag-item").text($(selected).text()).show();
  };
  $.extend(DragMovingState.prototype, AbstractState.prototype, {
      onMouseMove: function(event) {
      var target = event.target;
      if (target.tagName == "LI") {
        // プレビュー用のspanを移動
        $("#drag-item").css({left: event.pageX + 2, top: event.pageY + 2});
        // マウス直下項目のスタイルを変更
        $("#list li").removeClass("drag-over");
        $(target).addClass("drag-over");
      }
    },
    onMouseUp: function(event) {
      var target = event.target;
      $("#list li").removeClass("drag-over");
      // 項目を移動
      if (target !== selected) {
        $(target).after(selected);
      }
      // プレビュー用のspanを非表示
      $("#drag-item").hide();
      // 通常状態に戻る
      transitState(new NormalState());
    },
    toString: function() {
      return "DragMovingState";
    }
  });

  // TextEditingStateの定義
 TextEditingState = function(target) {
    selected = target;
    var pos = $(selected).position();
    // テキストフィールドを作成して表示
    this.input =
      $("<input type='text' class='text-editing'>")
      .css({left: pos.left, top: pos.top})
      .val($(selected).text())
      .appendTo("body")
      .focus();
    // 選択された項目の内容を消す
    $(selected).html("&nbsp;");
  };
  $.extend(TextEditingState.prototype, AbstractState.prototype, {
    onKeyDown: function(event) {
      if (event.which == 13) {  // enterキーだったら
        // 選択された項目の内容を更新
        var text = this.input.value;
        $(selected).text(this.input.val());
        // テキストフィールドを除去
        this.input.remove();
        // 通常状態に遷移
        transitState(new NormalState());
      }
    },
    toString: function() {
      return "TextEditingState";
    }
  });

  // 初期状態はNormalStateから開始
  transitState(new NormalState());
});
</script>
</head>
<body onselectstart="return false;"
      onmousedown="if (typeof event.preventDefault != 'undefined') { event.preventDefault(); }">
<ul id="list">
  <li>Item-1</li>
  <li>Item-2</li>
  <li>Item-3</li>
  <li>Item-4</li>
  <li>Item-5</li>
</ul>
<span id="drag-item">test</span>
Debug : <span id="debug"></span>
</body>
</html>