タッチベースのユーザーインターフェイスを高度にサポートするため、タッチスクリーンやトラックパッドでの指 (あるいはスタイラス) の動きを解釈する機能を、touch イベントが提供します。

タッチイベントのインターフェイスは、2 本の指によるジェスチャーなどアプリケーション固有のマルチタッチ操作をサポートするために使用できる、比較的低レベルの API です。マルチタッチ操作は、1 本の指 (またはスタイラス) を始めに接触面へタッチしたときから始まります。その後に他の指をタッチすることができ、さらに任意でタッチ面上で動かします。指を接触面から離すと、操作が終了します。操作している間、アプリケーションは開始・移動・終了の各段階中にタッチイベントを受け取ります。

タッチイベントはマウスイベントに似ていますが、タッチ面上の異なる場所で同時に発生するタッチをサポートすることが異なります。TouchEvent インターフェイスは、現在アクティブなすべてのタッチ個所を包含します。Touch インターフェイスはひとつのタッチ個所を表し、ブラウザのビューポートを基準にしたタッチ個所の位置などの情報を含みます。

定義

サーフェス
タッチに反応する面。スクリーンやトラックパッドでしょう。
タッチポイント
サーフェスに接触した点。これは指 (あるいはひじ、耳、鼻などでもよいのですが、たいてい指でしょう) またはスタイラスでしょう。

インターフェイス

TouchEvent
サーフェスでタッチ状態が変化したときに発生するイベントを表します。
Touch
ユーザーとタッチサーフェスが接した点 1 個を表します。
TouchList
タッチのグループを表します。例えば、ユーザーが複数の指を同時にサーフェス上に置いた場合に使用します。

ここでは一度に複数のタッチポイントを取得しており、ユーザーが一度に複数の指で <canvas> に描くことができるようになっています。このサンプルはタッチイベントをサポートするブラウザーのみで動作します。

注記: ここからはサーフェスへの接触を表すときに "指" という表現を使用しますが、当然ながらスタイラスなど他の接触法も使用できます。

canvas を生成する

<canvas id="canvas" width="600" height="600" style="border:solid black 1px;">
  Your browser does not support canvas element.
</canvas>
<br>
<button onclick="startup()">Initialize</button>
<br>
Log: <pre id="log" style="border: 1px solid #ccc;"></pre>

イベントハンドラーを設定する

ページを読み込むとき、以下の startup() 関数を <body> 要素の onload 属性で呼び出します (ただしこのサンプルでは、MDN の Live Example システムの制限によりボタンを押して呼び出します)。

function startup() {
  var el = document.getElementsByTagName("canvas")[0];
  el.addEventListener("touchstart", handleStart, false);
  el.addEventListener("touchend", handleEnd, false);
  el.addEventListener("touchcancel", handleCancel, false);
  el.addEventListener("touchmove", handleMove, false);
  log("initialized.");
}

これは単に <canvas> 要素へすべてのイベントリスナを設定している関数であり、タッチイベントの発生に応じてハンドリングできるようになります。

新たなタッチをトラッキングする

進行中のタッチをトラッキングし続けます。

var ongoingTouches = [];

サーフェス上で新たなタッチが発生したことを示す touchstart イベントが発生すると、handleStart() 関数を呼び出します。

function handleStart(evt) {
  evt.preventDefault();
  log("touchstart.");
  var el = document.getElementsByTagName("canvas")[0];
  var ctx = el.getContext("2d");
  var touches = evt.changedTouches;
        
  for (var i = 0; i < touches.length; i++) {
    log("touchstart:" + i + "...");
    ongoingTouches.push(copyTouch(touches[i]));
    var color = colorForTouch(touches[i]);
    ctx.beginPath();
    ctx.arc(touches[i].pageX, touches[i].pageY, 4, 0, 2 * Math.PI, false);  // a circle at the start
    ctx.fillStyle = color;
    ctx.fill();
    log("touchstart:" + i + ".");
  }
}

ここでは、ブラウザーがタッチイベントの処理を続けないようにするため event.preventDefault() を呼び出します (また、マウスイベントの伝達も抑止します)。そしてコンテキストを取得して、イベントの TouchEvent.changedTouches プロパティから変化したタッチポイントのリストを取り込みます。

その後に、リスト内のすべての Touch オブジェクトをイテレートしてアクティブなタッチポイントの配列に送り込み、描画を開始する位置に小さな丸印を描画します。この例では 4 ピクセル幅の線を使用しますので、半径 4 ピクセルの円がきれいに見えます。

タッチの移動に合わせて描画する

1 本以上の指が移動するたびに touchmove イベントが発生しますので、その結果 handleMove() 関数が呼び出されます。これはキャッシュしたタッチ情報を更新して、タッチごとに以前の位置から現在の位置まで線を描画する役割を担っています。

function handleMove(evt) {
  evt.preventDefault();
  var el = document.getElementsByTagName("canvas")[0];
  var ctx = el.getContext("2d");
  var touches = evt.changedTouches;

  for (var i = 0; i < touches.length; i++) {
    var color = colorForTouch(touches[i]);
    var idx = ongoingTouchIndexById(touches[i].identifier);

    if (idx >= 0) {
      log("continuing touch "+idx);
      ctx.beginPath();
      log("ctx.moveTo(" + ongoingTouches[idx].pageX + ", " + ongoingTouches[idx].pageY + ");");
      ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
      log("ctx.lineTo(" + touches[i].pageX + ", " + touches[i].pageY + ");");
      ctx.lineTo(touches[i].pageX, touches[i].pageY);
      ctx.lineWidth = 4;
      ctx.strokeStyle = color;
      ctx.stroke();

      ongoingTouches.splice(idx, 1, copyTouch(touches[i]));  // swap in the new touch record
      log(".");
    } else {
      log("can't figure out which touch to continue");
    }
  }
}

これは同様に変更されたタッチに対してイテレートしていますが、各タッチで新たに描画する線分の開始点を検出するために、各タッチの以前の情報についてキャッシュしたタッチ情報の配列を参照しています。これは、各タッチの Touch.identifier プロパティを確認して行います。このプロパティは各タッチで一意の識別子であり、指とタッチ面との接触が続いている間、値が固定されます。

これにより各タッチの前の位置の座標を取得して、2 つの点を結ぶ線分を描画するために適切なコンテキストメソッドを使用できます。

線分を描画した後、前のタッチ個所の情報を ongoingTouches 配列内にある現在の情報に置き換えるため、Array.splice() を呼び出します。

タッチの終了を制御する

ユーザーがタッチ面から指を離すと、touchend イベントが発生します。私たちはこれらの両方を、以下の handleEnd() 関数を呼び出すというひとつの方法で扱います。この関数の役割は、終了したタッチについて最後の線分を描画することと、継続中のタッチのリストからタッチ個所を削除することです。

function handleEnd(evt) {
  evt.preventDefault();
  log("touchend");
  var el = document.getElementsByTagName("canvas")[0];
  var ctx = el.getContext("2d");
  var touches = evt.changedTouches;

  for (var i = 0; i < touches.length; i++) {
    var color = colorForTouch(touches[i]);
    var idx = ongoingTouchIndexById(touches[i].identifier);

    if (idx >= 0) {
      ctx.lineWidth = 4;
      ctx.fillStyle = color;
      ctx.beginPath();
      ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
      ctx.lineTo(touches[i].pageX, touches[i].pageY);
      ctx.fillRect(touches[i].pageX - 4, touches[i].pageY - 4, 8, 8);  // and a square at the end
      ongoingTouches.splice(idx, 1);  // remove it; we're done
    } else {
      log("can't figure out which touch to end");
    }
  }
}

これは前の関数にとても似ていますが、終端を表す小さな四角形を描画することと、Array.splice() を呼び出して、更新後の情報を追加せずに継続中のタッチリストから古い項目を削除することが異なります。この結果、タッチ個所の追跡を停止します。

取り消されたタッチを制御する

ユーザーの指がブラウザーの UI に入り込んだり、その他にタッチをキャンセルしなければならないときには touchcancel イベントが発生して、以下の handleCancel() 関数が実行されます。

function handleCancel(evt) {
  evt.preventDefault();
  log("touchcancel.");
  var touches = evt.changedTouches;
  
  for (var i = 0; i < touches.length; i++) {
    var idx = ongoingTouchIndexById(touches[i].identifier);
    ongoingTouches.splice(idx, 1);  // remove it; we're done
  }
}

即座にタッチを取り消すという考え方から最終の線分を描画せずに、継続中のタッチリストから単純にタッチを削除します。

便利な関数

この例ではコードの残りの部分をより明確にすることを助ける、簡単に見ておくべきである 2 つの便利な関数を使用しています。

それぞれのタッチの色を選択する

それぞれのタッチの外見を区別して描画するために、タッチの一意な識別子を元に色を選択する colorForTouch() 関数を使用します。この識別子は不明瞭な数値ですが、少なくとも現在アクティブなタッチを区別することはできます。

function colorForTouch(touch) {
  var r = touch.identifier % 16;
  var g = Math.floor(touch.identifier / 3) % 16;
  var b = Math.floor(touch.identifier / 7) % 16;
  r = r.toString(16); // make it a hex digit
  g = g.toString(16); // make it a hex digit
  b = b.toString(16); // make it a hex digit
  var color = "#" + r + g + b;
  log("color for touch with identifier " + touch.identifier + " = " + color);
  return color;
}

この関数の戻り値は、描画色を設定するために <canvas> 関数を呼び出すときに使用できる文字列です。例えば Touch.identifier の値が 10 であれば、戻り値は文字列 "#aaa" になります。

touch オブジェクトをコピーする

一部のブラウザー (例えばモバイル版 Safari) はイベント間で touch オブジェクトを再使用するため、オブジェクト全体を参照するよりも、関心がある部分をコピーするほうが最善です。

function copyTouch(touch) {
  return { identifier: touch.identifier, pageX: touch.pageX, pageY: touch.pageY };
}

継続中のタッチを発見する

以下の ongoingTouchIndexById() 関数は、指定した識別にマッチするタッチを見つけるために配列 ongoingTouches を探索して、そのタッチの配列内における添字を返します。

function ongoingTouchIndexById(idToFind) {
  for (var i = 0; i < ongoingTouches.length; i++) {
    var id = ongoingTouches[i].identifier;
    
    if (id == idToFind) {
      return i;
    }
  }
  return -1;    // not found
}

何を行っているか表示する

function log(msg) {
  var p = document.getElementById('log');
  p.innerHTML = msg + "\n" + p.innerHTML;
}

ブラウザーがサポートしていれば、実際に試す ことができます。

jsFiddle example

追加の Tips

この章ではウェブアプリケーションでタッチイベントを扱う方法について、追加の Tips を紹介します。

クリックを制御する

touchstart あるいは一連の中で最初の touchmovepreventDefault() を呼び出すと対応するマウスイベントの発生を抑制できるため、touchstart よりも touchmovepreventDefault() を呼び出すことが一般的です。この方法では従来どおりマウスイベントが発生して、リンクなどが引き続き動作します。代わりに一部のフレームワークでは同様の目的で、タッチイベントをマウスイベントとして再発生させています。(この例は過度に単純化しており、奇妙な動作になるかもしれません。ガイドとして掲載しているに過ぎません。)

function onTouch(evt) {
  evt.preventDefault();
  if (evt.touches.length > 1 || (evt.type == "touchend" && evt.touches.length > 0))
    return;

  var newEvt = document.createEvent("MouseEvents");
  var type = null;
  var touch = null;

  switch (evt.type) {
    case "touchstart": 
      type = "mousedown";
      touch = evt.changedTouches[0];
      break;
    case "touchmove":
      type = "mousemove";
      touch = evt.changedTouches[0];
      break;
    case "touchend":        
      type = "mouseup";
      touch = evt.changedTouches[0];
      break;
  }

  newEvt.initMouseEvent(type, true, true, evt.originalTarget.ownerDocument.defaultView, 0,
    touch.screenX, touch.screenY, touch.clientX, touch.clientY,
    evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey, 0, null);
  evt.originalTarget.dispatchEvent(newEvt);
}

2 番目のタッチのみで preventDefault() を呼び出す

ページ上で pinchZoom と言った操作を防ぐテクニックのひとつとして、一連のタッチの 2 番目で preventDefault() を呼び出す方法があります。この動作はタッチイベントの仕様書で明示されておらず、ブラウザーによって結果が異なります (iOS ではズームを防ぎますが、パンは可能です。Android はズームが可能ですが、パンはできません。Opera および Firefox は現状、パンもズームも防ぎます)。現在、このケースで特定の動作に依存することは推奨されず、メタビューポートのズームを防ぐと考えてください。

仕様

仕様書 策定状況 コメント
Touch Events – Level 2
Touch の定義
ドラフト radiusXradiusYrotationAngleforce プロパティを追加。
Touch Events
Touch の定義
勧告 最初期の定義

ブラウザー実装状況

Touch

タッチイベントは通常、タッチスクリーンを備えたデバイスで使用できますが、多くのブラウザは、タッチスクリーンを備えたものであっても、すべてのデスクトップデバイスでタッチイベントAPIを使用できません。

これは、一部のWebサイトではタッチイベントAPIの一部の可用性がブラウザがモバイルデバイスで実行されていることを示す指標として使用されているためです。タッチイベントAPIが利用可能な場合、これらのWebサイトはモバイルデバイスを想定し、モバイルに最適化されたコンテンツを配信します。その結果、タッチスクリーンを搭載したデスクトップデバイスのユーザーにとっては、使い勝手が悪くなる可能性があります。

すべての種類のデバイスでタッチとマウスの両方をサポートするには、代わりにポインタイベント(pointer events)を使用してください。

Update compatibility data on GitHub
デスクトップモバイル
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung Internet
TouchChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
Touch() constructor
実験的
Chrome 完全対応 48Edge 未対応 なしFirefox 完全対応 46IE 未対応 なしOpera 完全対応 35Safari 未対応 なしWebView Android 完全対応 48Chrome Android 完全対応 48Firefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
clientXChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
clientYChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
force
実験的
Chrome 完全対応 ありEdge ? Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
identifierChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
pageXChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
pageYChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
radiusX
実験的
Chrome 完全対応 43Edge ? Firefox ? IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 43Chrome Android 完全対応 43Firefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
radiusY
実験的
Chrome 完全対応 43Edge ? Firefox ? IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 43Chrome Android 完全対応 43Firefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
rotationAngle
実験的
Chrome 完全対応 43Edge ? Firefox ? IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 43Chrome Android 完全対応 43Firefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
screenXChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
screenYChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり
targetChrome 完全対応 22Edge 完全対応 ありFirefox 完全対応 52
補足
完全対応 52
補足
補足 Touch events support has been fixed and reenabled in Windows desktop platforms.
未対応 18 — 24
補足
補足 Web compatibility issues seen in bug 888304.
IE 未対応 なしOpera 完全対応 ありSafari 未対応 なしWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 6Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 あり

凡例

完全対応  
完全対応
未対応  
未対応
実装状況不明  
実装状況不明
実験的。動作が変更される可能性があります。
実験的。動作が変更される可能性があります。
実装ノートを参照してください。
実装ノートを参照してください。

Firefox, touch events, and multiprocess (e10s)

Firefoxでは、e10s(electrolysis;multiprocess Firefox)が無効になっていると、タッチイベントは無効になります。Firefoxではe10sがデフォルトでオンになっていますが、e10sが機能しないようにする必要がある特定のアクセシビリティツールやFirefoxアドオンがインストールされている場合など、特定の状況で無効になることがあります。つまり、タッチスクリーン対応のデスクトップ/ラップトップでも、タッチイベントは有効になりません。

e10sが無効になっているかどうかをテストするには、about:supportに移動し、 "Application Basics"セクションの "Multiprocess Windows"エントリを調べます。1/1は有効、0/1は無効を意味します。

タッチイベントのサポートを明示的に再度有効にするためにe10sを強制的にオンにしたい場合は、about:configに移動して新しいブール設定browser.tabs.remote.force-enableを作成する必要があります。trueに設定してブラウザを再起動すると、他の設定に関係なくe10sが有効になります。

ドキュメントのタグと貢献者

このページの貢献者: isdh, mdnwebdocs-bot, mfuji09, yyss, Sebastianz, ethertank, Potappo
最終更新者: isdh,