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

  • touchstartイベントが発生したときの座標を覚えておく。
  • touchmoveイベントで、軌跡を記録。
  • touchendイベントで、移動距離を計算。
  • 一定の範囲内なら、clickとみなす。

その1。

<!DOCTYPE html>
<html lang="ja">
<head>
  <title></title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=640">
  
  <style>
  </style>
</head>

<body>
<h1>iPod Touch & iPad Click Test</h1>
<p>
  click イベントが使えない。しかも touchendイベントの時に座標を得られないそうだ。
<p>
  なので、touchmoveイベントも使いながら座標を記憶する

<script>

(function () {
  
  function Click (node, cbFunc, cbObject, arg) {
    this.node = node;
    this.cbFunc = cbFunc;
    this.range = 10;
    this.cbObject = cbObject;
    this.arg = arg;
    this.event = null;
    this.startPoint = { 'x' : null, 'y' : null };
    this.currentPoint = { 'x' : null, 'y' : null };
  }
  
  function addEvent () {
    this.node.addEventListener ('touchstart', this, false);
    this.node.addEventListener ('touchmove', this, false);
    this.node.addEventListener ('touchend', this, false);
  }
  
  
  function handler (event) {
    var touch = event.touches;

    if (1 < touch.length)
      return;

    if((touch = touch[0])) {
      switch (event.type) {
      case 'touchstart' :
        this.currentPoint.x = this.startPoint.x = touch.pageX;
        this.currentPoint.y = this.startPoint.y = touch.pageY;
        this.event = event;
        break;
      
      case 'touchmove' :
        this.currentPoint.x = touch.pageX;
        this.currentPoint.y = touch.pageY;
        break;
      
      case 'touchend' :
        if (this.range > (Math.abs (this.currentPoint.x - this.startPoint.x)))
        if (this.range > (Math.abs (this.currentPoint.y - this.startPoint.y))) {
          this.cbFunc.apply (this.cbObject, [this.event].concat (this.arg));
        }
        break;
      }
    }
  }
  
//_____________
  
  function createHandler (node, cbFunc, cbObject /*, thisp*/) {
    var obj, arg;
    
    if (2 > arguments.length)
      return null;
    if (1 !== node.nodeType && 9 !== node.nodeType)
      return null;
    if ('function' !== typeof cbFunc)
      if (('object' !== typeof cbFunc) && ! ('handleEvent' in cbFunc))
        return null;
    
    arg = Array.prototype.slice.call (arguments, 3);
    obj = new Click (node, cbFunc, cbObject, arg);
    obj.addEvent ();
    
    return obj;
  }

//_____________
  
  Click.prototype.addEvent = addEvent;
  Click.prototype.handleEvent = handler;
  
  this.addClickHandler = createHandler;
})();

var func = function (a, b ,c) { alert (["Click", this, a, b, c]); };
var clickEvent = addClickHandler (document, func, 1, 2 , 3);

</script>


その2.
this.node を不要に。(私的所有物なら、「なにかのために取っておこう」と思うのだが、ここは、ばっさり)

(function () {
  
  function Click (cbFunc, cbObject, arg) {
    this.cbFunc = cbFunc;
    this.range = 10;
    this.cbObject = cbObject;
    this.arg = arg;
    this.event = null;
    this.startPoint = { 'x' : null, 'y' : null };
    this.currentPoint = { 'x' : null, 'y' : null };
  }
  
  
  function handler (event) {
    var touch = event.touches;

    if (1 < touch.length)
      return;

    if((touch = touch[0])) {
      switch (event.type) {
      case 'touchstart' :
        this.currentPoint.x = this.startPoint.x = touch.pageX;
        this.currentPoint.y = this.startPoint.y = touch.pageY;
        this.event = event;
        break;
      
      case 'touchmove' :
        this.currentPoint.x = touch.pageX;
        this.currentPoint.y = touch.pageY;
        break;
      
      case 'touchend' :
        if (this.range > (Math.abs (this.currentPoint.x - this.startPoint.x)))
        if (this.range > (Math.abs (this.currentPoint.y - this.startPoint.y))) {
          this.cbFunc.apply (this.cbObject, [this.event].concat (this.arg));
        }
        break;
      }
    }
  }
  
//_____________
  
  function createHandler (node, cbFunc, cbObject /*, thisp*/) {
    var obj, arg;
    
    if (2 > arguments.length)
      return null;
    if (1 !== node.nodeType && 9 !== node.nodeType)
      return null;
    if ('function' !== typeof cbFunc)
      if (('object' !== typeof cbFunc) && ! ('handleEvent' in cbFunc))
        return null;
    
    arg = Array.prototype.slice.call (arguments, 3);
    obj = new Click (cbFunc, cbObject, arg);

    node.addEventListener ('touchstart', obj, false);
    node.addEventListener ('touchmove', obj, false);
    node.addEventListener ('touchend', obj, false);
    
    return obj;
  }

//_____________
  
  Click.prototype.handleEvent = handler;
  
  this.addClickHandler = createHandler;
})();

その3。

タッチの数が1の時だけ実行するようにしていたが、touchend イベントが実行されているときは、タッチ数が0。後で直さなきゃ〜!

<!DOCTYPE html>
<title></title>
  <meta name="viewport" content="width=640">
  <style>
  p.focus {
    border: 1px red solid;
  }
  </style>
<body>

<h1>iPod Touch & iPad Click Test</h1>
<p class="focus">
  click イベントが使えない。しかも touchendイベントの時に座標を得られないそうだ。
<p id="test">
  なので、touchmoveイベントも使いながら座標を記憶する
<p>
  click イベントが使えない。しかも touchendイベントの時に座標を得られないそうだ。
<p id="test">
  なので、touchmoveイベントも使いながら座標を記憶する
<p>
  click イベントが使えない。しかも touchendイベントの時に座標を得られないそうだ。
<p id="test">
  なので、touchmoveイベントも使いながら座標を記憶する
<input type="text" id="tt">

<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.currentPoint = { 'x' : 0, 'y' : 0 };
  }
  
  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.currentPoint.x,   // スクリーン内のマウスの X 座標
      this.currentPoint.y,   // スクリーン内のマウスの Y 座標
      this.currentPoint.x,   // ブラウザ表示域内のマウスの X 座標
      this.currentPoint.y,   // ブラウザ表示域内のマウスの Y 座標
      false,                 // Ctrl キーが押されているか
      false,                 // Alt キーが押されているか
      false,                  // Shift キーが押されているか
      false,                 // Meta キーが押されているか
      0,                     // どのボタンが押されているか(左から順に 0、1、2)
      doc.body               // 関連するノード(何でも良い)
    );

    e.dispatchEvent(evt);
  }
  
  function handler (event) {
    var touch = event.touches;

    switch (event.type) {
    case 'touchstart' :
      if (1 < touch.length)
        return;
      
      if ((touch = touch[0])) { // スタートとカーレント位置を同期させる
        this.currentPoint.x = this.startPoint.x = touch.pageX;
        this.currentPoint.y = this.startPoint.y = touch.pageY;
      }
      break;
    
    case 'touchmove' :
      if ((touch = touch[0])) { // カーレント位置を記憶
        this.currentPoint.x = touch.pageX;
        this.currentPoint.y = touch.pageY;
      }
      break;
    
    case 'touchend' :
      if (this.range > (Math.abs (this.currentPoint.x - this.startPoint.x)))
        if (this.range > (Math.abs (this.currentPoint.y - this.startPoint.y)))
          fire.call (this, event);
      break;
    }
  }
  
  
  // クリック・エミュレータ初期化
  function emulator_init (view) {
    var doc = view ? view.document: document;
    var 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 (this);

</script>