Svelteアプリでクリックイベントのスマホ対応〜preventDefaultでタッチイベントを適切にさばく


※ 当ページには【広告/PR】を含む場合があります。
2023/02/21
【SvelteでRPGゲーム開発】Svelteコンポーネントを使ったHTML中の表示画面のフレキシブル対応
タコキンのPスクール|Svelteアプリでクリックイベントのスマホ対応〜preventDefaultでタッチイベントを適切にさばく



フルHTMLによるアプリ開発のメリットは、ブラウザからであればどのようなデバイスからでも等しく同じ動作をするアプリを提供できることです。
デバイスとは言っても、大きく分けて「デスクトップ型」と「スマホ型」に区分できます。
当然この2つはハードウェアスペックの違いがあるのは仕方ないとして、大きく違うのが
ポインターインターフェース 、つまり「マウス」か「タッチパネル」かにあります。
HTMLでブラウザゲームを作る場合、このインターフェースによる入力の差異を意識してソースコードを実装しなくてはいけなくなります。
今回は、SvelteでHTMLアプリを作るときに知っておきたい、クリックイベントのスマホ対応術を解説していきます。
この記事で目的としてかかげるポイントは以下の通りです。

            1. タッチイベントのデフォルト抑制(preventDefault)を理解する
2. 古いブラウザへのタッチイベント対応を考える
3. Svelteの{#if}構文で要素を柔軟に切り替えてイベントハンドリングする

        

合同会社タコスキングダム|蛸壺の技術ブログ【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方

touch系イベントをpreventDefaultする

「タッチイベント」 は各ブラウザで提供されているWeb APIの一つです。
主にタッチパネルデバイスが検知されたモバイルモードのときにブラウザが自動でタッチイベントを有効化してくれます。

参考|タッチイベント - MDN Web Doc

タッチイベントを使うときに問題になるのが、スマホなどのタッチパネル対応デバイスで、
クリックイベントとタッチイベントが競合してしまう ことです。
ブラウザからするとクリックと画面タッチの区別ができないので、最悪同じイベントを同時に2回発生させてしまいます。

clickイベント touchイベント
デスクトップ
モバイル

クリックイベントとはそもそもマウス操作を想定したイベントですので、モバイルのときにクリックイベントが発生されても困ります。
そこで、ブラウザがモバイルモードのときにタッチイベントを適切に取り扱えるように、
「preventDefault」 が備わっています。
イベントごとにpreventDefaultは異なるのですが、タッチイベントのデフォルト抑制とは主に下の2つになります。

            1. タッチイベント処理を続けないようにする
2. クリックイベント(マウス)の伝達を抑止する

        

現状ほとんどのブラウザがタッチイベントのpreventDefaultに対応しているので、スマホへのクリックイベント対応がこれだけで済むようになっています。
この点を踏まえて、Svelteコードへタッチイベントを仕込んでみましょう。
Svelteへの対応はとてもシンプルで、
別記事で作成しているアプリの例 でいうと、 mousedownmouseup の2つのイベントハンドラと被せる形で、 touchstarttouchend も追加します。

            ...
<button class="position-top btn cross-key-btn"
    on:mousedown={() => cursorHandler(1,0)}
    on:mouseup={() => cursorHandler(1,1)}
    on:touchstart|preventDefault={() => cursorHandler(1,0)}
    on:touchend|preventDefault={() => cursorHandler(1,1)}><span class="top-mark"></span>
</button>
...

        

やっていることは
on:mousedownon:touchstart|preventDefaulton:mouseupon:touchend|preventDefault でそれぞれ拡張させて、クリックイベントを持つ全ての要素に追加していくだけです。
これだけでクリックイベントのスマホ対応は完了です。

preventDefalut の理屈を知っていたら簡単ですね。


合同会社タコスキングダム|蛸壺の技術ブログ【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方

古いブラウザへのタッチイベント対応



現状ではほとんどのブラウザで対応していると予測しますが、古いAndroidデバイスなどのブラウザではタッチイベントの
preventDefault には対応していない恐れがあります。
そこで、クリックイベントとタッチイベントを適切なデバイスごとに切り替えるための実装を自前で対処します。
やることは先程の
preventDefault の考え方と同じで、 モバイルモードのときにクリックイベントが無効化 してあげているだけです。
まずSvelteの場合、タッチイベントがブラウザで有効かどうかを任意のコンポーネントのスクリプト部でチェックすることができます。

            <script lang="ts">
    import { onMount } from 'svelte';

    onMount(() => {
        let isMobile = "ontouchstart" in window;
        if (isMobile) {
            console.log('タッチイベントが使えます');
        } else {
            console.log('タッチイベントが使えません');
        }
    });
</script>
...

        

この
isMobile を使って、クリックイベントのときのハンドラと、タッチイベントのハンドラを2つ用意して、モバイルモードのときにクリックイベントがスキップされるようにスクリプトを実装します。

            function onClick() {
    if (!isMoble) {
        //何らかのイベント処理
        hoge();
    }
}

function onTouch() {
    //何らかのイベント処理
    hoge();
}

        

もちろん古いブラウザまでサポートするかどうかは、開発者の判断におまかせします。


合同会社タコスキングダム|蛸壺の技術ブログ【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方

Svelteでデバイス対応を細かく切り分けるときのベストプラクティス



先程取り上げたスマホ対応の実装をもう一度良く眺めてみましょう。

            ...
<button class="position-top btn cross-key-btn"
    on:mousedown={() => cursorHandler(1,0)}
    on:mouseup={() => cursorHandler(1,1)}
    on:touchstart|preventDefault={() => cursorHandler(1,0)}
    on:touchend|preventDefault={() => cursorHandler(1,1)}><span class="top-mark"></span>
</button>
...

        


このイベントを一つの要素に詰め込んだ書式をみて、パッと「デスクトップ」と「スマホ」の両方いっぺんに対処する、と他の人から理解するのは少しむずかしいはずです。
つまり、一つの要素に多すぎるイベントを追加することがあまりいい書き方になっていません。
ではSvelteの場合どうするのが良いかというと、
{#if}〜 で構造分岐構文を利用します。
先程の例で置き換えると、

            {#if isMobile}
<!-- モバイルモードの場合にはこっち -->
<button class="position-top btn cross-key-btn"
    on:touchstart|preventDefault={() => cursorHandler(1,0)}
    on:touchend|preventDefault={() => cursorHandler(1,1)}><span class="top-mark"></span></button>
{:else}
<!-- 通常のデスクトップモード -->
<button class="position-top btn cross-key-btn"
    on:mousedown={() => cursorHandler(1,0);}
    on:mouseup={() => cursorHandler(1,1);}>▲</span></button>
{/if}

        

のようになります。
Svelteで開発する場合、こちらの方がイベントの身通しがよくなります。



合同会社タコスキングダム|蛸壺の技術ブログ【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方

まとめ



以上、今回の記事の話をまとめると、以下のようなになります。

            1. タッチイベントのデフォルト抑制(preventDefault)を理解する
2. 古いブラウザへのタッチイベント対応を考える
3. Svelteの{#if}構文で要素を柔軟に切り替えてイベントハンドリングする

        

一昔と比べて、スマホのブラウザもモダンなWeb APIの機能に対応してはきました。
しかし依然として細やかな操作を実現したいなら、デバイス向けの専用アプリを作るほうに一日の長がある感は否めません。
専用アプリには専用アプリの、ブラウザアプリにはブラウザアプリの良さ・悪さがありますので、開発の選択肢も複数持たれておくと良いと思います。