【スマホ対応】jQuery 連続スワイプイベントをハンドリングする方法

スマートフォンなどで、連続スワイプのハンドリング方法をまとめる。

指が触れ始めたときのDOMや、離れた瞬間のDOMは簡単に取得できるが
「今」指が触れているDOMを取得するのに苦戦した。

先日行った[ホームページのデザインリニューアル](http://yutarotanaka.com/)でも
この方法を使ってスマホのスワイプ対応をした。

# やりたいこと
「このような要素をタッチしたときに、触れたところの色を変えたい」
「ひとつひとつタップするのではなく、指でなぞった部分の色を変えたい」

![TouchSequence.png](https://qiita-image-store.s3.amazonaws.com/0/31862/fe3384c2-971f-ac9e-b890-d3d13689f82a.png)

タッチ対象は、下記のような spanタグを用意して、CSSで成形する。

```html:html
<span class="panel">1</span>
<span class="panel">2</span>
 ・・・
<span class="panel">20</span>
```

# （PC）mouseenter イベントをハンドリングする

$(‘.panel’) でタッチ対象の要素を取得し、.on() で mouseenter イベントにイベントハンドラをセットする。

ハンドラ内では、タッチ対象が class = “panel” を持っているとき、背景色を変える。
（今回は class = “green” に対して CSS で背景色をつけている）

```js:js
$().ready(function () {
    $('.panel').on({
        'mouseenter': function () {
            var target = $(this);
            if (target.hasClass('panel')) {
                target.addClass('green');
            }
        }
    });
});
```

マウスが触れた瞬間に、その要素の色が変化する。

![TouchSequence-2.png](https://qiita-image-store.s3.amazonaws.com/0/31862/8ad21fd9-d13e-e838-0bac-1243ca2af474.png)

PCは余裕

# （スマホ）touchstart, touchend, touchmove など → ×

touchstart, touchend, touchmove などのイベントが発生した時の要素の要素取得を試みた。

ハンドラ内の記述「var target = $(this); 」で取得できるのは、タッチした瞬間の要素。
そのため、スマホに指をつけたときの要素は色が変るけど、指を放さずに画面上を動かしても、target は変化しない。

```js:js
$().ready(function () {
 $('.panel').on({
        'touchmove': function (event) {
            var target = $(this);
            if (target.hasClass('panel')) {
                target.addClass('green');
            }
        }
    });
});
```

また、event オブジェクトの持つ event.originalEvent の、changedTouches, currentTarget, target, targetTouches, touches, … などでは、指が触れているDOMは取得できず、結果は同じ。

```js:js
$().ready(function () {
 $('.panel').on({
        'touchmove': function (event) {
            var target = event.XXXXXXX;
            if (target.hasClass('panel')) {
                target.addClass('green');
            }
        }
    });
});
```

# （スマホ）touchmove + position + elementFromPoint → ○

試行錯誤した結果、
「touchmove イベント時のハンドラ内で、event が起こったポジションを取得し、そのポジションにある要素を取得する。」
これで解決した。

touchmove イベントは、タッチデバイスの画面を指でなぞっている間連続して発生する。
その際に、event オブジェクトで取得できる指の座標をdocument.elementFromPoint(X, Y) に渡す。
これによって、指定された座標上にある要素を取得できる

```js:js
$().ready(function () {
    $('.panel').on({
        'touchmove': function (event) {
            var touch = event.originalEvent.touches[0];
            var target = $(document.elementFromPoint(touch.clientX, touch.clientY));
 
            if (target.hasClass('panel')) {
                target.addClass('green');
            }
        }
  });
```

# まとめ
最終的には下記の通り、mouseenter, touchmove イベントにそれぞれハンドラをセットすることで、PCでもスマートフォンでも連続タッチイベントが取得できた。

```js:js
$('.panel').on({
    'mouseenter': function () {
        var target = $(this);
        if (target.hasClass('panel')) {
            target.addClass('green');
        }
    },
    'touchmove': function (event) {
        var touch = event.originalEvent.touches[0];
        var target = $(document.elementFromPoint(touch.clientX, touch.clientY));
 
        if (target.hasClass('panel')) {
            target.addClass('green');
        }
    }
});
```

イベントをハンドルするときに、**座標からアプローチする**という発想がなかなか出てこなかったので、メモ。

- [サンプル](http://jsfiddle.net/tanaka_yutaro/dF4Ly/2/)
- [活用例](http://yutarotanaka.com/)