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


※ 当ページには【広告/PR】を含む場合があります。
2023/02/21
【SvelteでRPGゲーム開発】Svelteコンポーネントを使ったHTML中の表示画面のフレキシブル対応
合同会社タコスキングダム|TacosKingdom,LLC.

フルHTMLによるアプリ開発のメリットは、ブラウザからであればどのようなデバイスからでも等しく同じ動作をするアプリを提供できることです。

デバイスとは言っても、大きく分けて「デスクトップ型」と「スマホ型」に区分できます。

当然この2つはハードウェアスペックの違いがあるのは仕方ないとして、大きく違うのが
ポインターインターフェース、つまり「マウス」か「タッチパネル」かにあります。

HTMLでブラウザゲームを作る場合、このインターフェースによる入力の差異を意識してソースコードを実装しなくてはいけなくなります。

今回は、SvelteでHTMLアプリを作るときに知っておきたい、クリックイベントのスマホ対応術を解説していきます。

この記事で目的としてかかげるポイントは以下の通りです。

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


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

すぐに作れる ずっと使える Inkscapeのすべてが身に付く本

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の理屈を知っていたら簡単ですね。


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

すぐに作れる ずっと使える Inkscapeのすべてが身に付く本

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

現状ではほとんどのブラウザで対応していると予測しますが、古い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();
}
        
もちろん古いブラウザまでサポートするかどうかは、開発者の判断におまかせします。


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

すぐに作れる ずっと使える Inkscapeのすべてが身に付く本

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で開発する場合、こちらの方がイベントの身通しがよくなります。


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

すぐに作れる ずっと使える Inkscapeのすべてが身に付く本

まとめ

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

            
            1. タッチイベントのデフォルト抑制(preventDefault)を理解する
2. 古いブラウザへのタッチイベント対応を考える
3. Svelteの{#if}構文で要素を柔軟に切り替えてイベントハンドリングする
        
一昔と比べて、スマホのブラウザもモダンなWeb APIの機能に対応してはきました。

しかし依然として細やかな操作を実現したいなら、デバイス向けの専用アプリを作るほうに一日の長がある感は否めません。

専用アプリには専用アプリの、ブラウザアプリにはブラウザアプリの良さ・悪さがありますので、開発の選択肢も複数持たれておくと良いと思います。