iPod & iPad & iPhoneで、クリックイベントを発火させて、それを使えるようにする! その2。

<!DOCTYPE html>
<title></title>
  <meta name="viewport" content="width=640">
  <style>
  h1 {
    font-size: large;
  }
  h3 {
    margin-bottom : 0;
  }
  h3 + p {
    font-size: small;
    color: gray;
    margin : 0 2em;
  }
  em {
    color: green;
    font-size:x-large;
  }
  li.focus {
    border-bottom : 2px red dotted;
  }
  </style>
<body>

<h1>
  <em>iPod</em> &amp;
  <em>iPad</em> &amp;
  <em>iPhone</em> で、クリックイベントを有効にする
</h1>

<h3>クリックイベントが発生する状態</h3>
<ul>
  <li>document をクリック(タップ)した時
  <li>フォームの input要素(button)など
  <li>要素のインラインに書いたイベント
</ul>

<h3>クリックイベントの代わりに、touchstart, touchmove, touchend を利用する</h3>
<p>(3つのイベントを組み合わせて、クリックイベントを実現する)
<ul>
  <li>touchstart イベントが発生したら、スタート位置を記憶する
  <li>touchmove を監視し、現在位置との差が範囲外なら、止める
  <li>touchend イベントオブジェクトからは、位置が拾えないので、スタート位置を基準とし、
      クリックイベントを発生させる
</ul>

<h3>使い方</h3>
<ul>
  <li>
    <p>面倒なので略。
</ul>


<script>

document.addEventListener ('click', function (e) {
  var n = e.target;
  n.className = n.className ? '': 'focus';
}, false);

//_________________________________

(function () {

  function Emulator () {
    this.range        = 10;
    this.startPoint   = { 'x' : 0, 'y' : 0 };
    this.action       = true;
    this.state        = false;
  }
  

  function fire (event) {
    var e = event.target;
    var doc = e.ownerDocument;
    var evt = doc.createEvent ('MouseEvents');
    
    if (3 === e.nodeType)
      e = e.parentNode;
    
    evt.initMouseEvent(
      'click',               // イベント名
      true,                  // バブリングするか
      true,                  // デフォルトアクションが取り消し可能か
      doc.defaultView,       // イベントが発生したビュー
      1,                     // クリック回数
      this.startPoint.x,   // スクリーン内のマウスの X 座標
      this.startPoint.y,   // スクリーン内のマウスの Y 座標
      this.startPoint.x,   // ブラウザ表示域内のマウスの X 座標
      this.startPoint.y,   // ブラウザ表示域内のマウスの Y 座標
      false,                 // Ctrl キーが押されているか
      false,                 // Alt キーが押されているか
      false,                  // Shift キーが押されているか
      false,                 // Meta キーが押されているか
      0,                     // どのボタンが押されているか(左から順に 0、1、2)
      doc.body               // 関連するノード(何でも良い)
    );

    e.dispatchEvent(evt);
  }

  
  function handler (event) {
    if (! this.action)
      return;

    var touch = event.touches;
    var eType = event.type;
    var range = this.range;
    var point = this.startPoint;
    
    if (this.state) {
      if (('touchmove' === eType) && (touch = touch[0])) {
        if ((Math.abs (touch.pageX - point.x) < range) ||
            (Math.abs (touch.pageY - point.y) < range)) {
              this.state = false;
        }
      }
      else if ('touchend' === eType) {
        fire.call (this, event);
      }
    }
    else if (('touchstart' === eType) && (1 === touch.length) && (touch = touch[0])) {
      this.startPoint.x = touch.pageX;
      this.startPoint.y = touch.pageY;
      this.state = true;
    }
  }


  // クリック・エミュレータ初期化
  function emulator_init (view) {
    var doc = view ? view.document: document;
    var emu = null;
    
    if (/iPhone|iPad|iPod/.test (navigator.userAgent)) {
      emu = new Emulator;
      doc.addEventListener ('touchstart', emu, false);
      doc.addEventListener ('touchmove', emu, false);
      doc.addEventListener ('touchend', emu, false);
    }

    return emu;
  }
  
  Emulator.prototype.handleEvent = handler;
  Emulator.init = emulator_init;
  
  this.ClickEmulator = Emulator;
})();

ClickEmulator.init ();

</script>