スマホで触ると画面がスクロールしてしまうゲームの直し方
遊びをつくる執筆にClaudeを使用しています。

スマホでブラウザゲームを開いたとき、画面を指でタップしたりスワイプしたりするとゲームではなくページ全体がスクロールしてしまう——このトラブルは、PCで動作確認を済ませたゲームをスマホで試したときに最も起きやすい問題のひとつです。ゲームとしての操作を受け取るべき場面で、ブラウザが「ページを動かす操作」と解釈してしまうのが原因です。 直し方はいくつかのアプローチに分かれますが、どれを使うかは「どの要素でどんな操作を受け取りたいか」によって変わります。この記事では、原因の仕組みを短く整理したうえで、状況別に対処法と判断基準を示します。
この記事の目次
なぜスクロールが起きるのか
スマートフォンのブラウザは、タッチ操作に対してデフォルトで「スクロール・ピンチズーム」などの画面操作を割り当てています。JavaScriptの touchstart / touchmove イベントは発火しますが、ブラウザはその処理と並行して自分自身のスクロール動作も実行しようとします。
ゲームのコードが touchmove を受け取っていても、preventDefault() を呼ばない限りブラウザはスクロールを止めません。さらに近年のブラウザはパフォーマンス上の理由から、タッチイベントリスナーをデフォルトで「パッシブ(passive)」として扱うようになっています。パッシブなリスナーの中では preventDefault() が無効になるため、「書いたのに効かない」という状況が起きます。これがこの問題の中核にある仕組みです。
CSSだけで解決できるケース
ゲームのプレイ領域全体が一つの要素(<canvas> や特定の <div>)に収まっている場合、CSSの touch-action プロパティが最もシンプルな解決策です。
canvas {
touch-action: none;
}touch-action: none を指定した要素は、ブラウザのデフォルトタッチ操作(スクロール・ズーム)をすべて無効にします。JavaScriptのコードを変えずに済むため、既存のゲームへの影響が小さいのが利点です。
判断基準として、次の条件がそろうときはCSSだけで対応できます。
- ゲームの入力を受け取る要素が明確に一つに絞れる
- その要素の外側(余白・ヘッダーなど)はスクロールさせて構わない
- ページ全体のスクロールを完全に止める必要がない
逆に、ゲームが <body> 全体に広がっていたり、複数の要素にタッチ処理が分散していたりする場合は、後述のJavaScriptによる対処が必要になります。
JavaScriptで `preventDefault()` を正しく呼ぶ
CSS対応だけでは不十分なとき、またはすでに touchmove イベントで処理を書いているときは、イベントリスナーを「ノンパッシブ」として登録し直す必要があります。
element.addEventListener('touchmove', function(e) {
e.preventDefault();
// ゲームの処理
}, { passive: false });{ passive: false } を第三引数に渡すことで、ブラウザに「このリスナーは preventDefault() を呼ぶ可能性がある」と伝えます。これを書かないと、ブラウザはリスナーをパッシブとみなし、preventDefault() を呼んでもスクロールが止まりません。
touchstart でも同様です。ゲーム開始時の入力を受け取るイベントに { passive: false } が抜けていると、最初のタッチでページが動いてしまいます。
よくある失敗パターン
- addEventListener('touchmove', handler) と書いていて第三引数がない→ブラウザがパッシブと判断し preventDefault() が無視される
- e.preventDefault() はあるが touchstart にも touchmove にも両方必要な場面で片方しか書いていない
- canvas ではなく document や window にリスナーを付けていて、touch-action: none の範囲外のタッチが拾えていない
`<meta>` タグによるズーム制御との違い
<meta name="viewport" content="user-scalable=no"> を使うとピンチズームを止めることはできますが、スクロールは止まりません。「ズームだけ問題」と「スクロールも問題」は別の現象なので、viewportタグだけで対応しようとするとスクロールは残ります。また、ズーム禁止はアクセシビリティ上の懸念があり、ブラウザ側で無視されるケースも増えています。この方法に頼るより、touch-action とイベントリスナーの組み合わせで対処する方が確実です。
ゲーム全体をスクロール不可にしたいとき
ゲームがページ全体を占有する設計(いわゆるフルスクリーンゲーム)の場合、<body> 自体のスクロールを止めるのが最も確実です。
html, body {
overflow: hidden;
height: 100%;
touch-action: none;
}これに加えて、ゲームのメインキャンバスや操作領域にも touch-action: none を重ねて指定しておくと、ブラウザ間の挙動の差を減らせます。
ただし、この方法はページ内に「スクロールさせたいUI(例:スコアランキングの一覧)」があるときには使えません。その場合は、スクロールさせたい領域だけに overflow-y: auto を指定し、ゲームキャンバスとUIを明確に分離する構造にする必要があります。
確認と検証の手順
修正後は必ず実機で確認します。ブラウザのデベロッパーツールにあるデバイスシミュレーターはタッチイベントの挙動が実機と異なることがあるため、スクロール問題の確認には不向きです。
- スマートフォンの実機(iOS Safari・Android Chromeの両方)でゲームを開く
- ゲームの操作エリアを指でゆっくりスワイプし、ページが動かないことを確認する
- 二本指でピンチし、ズームが起きないことを確認する
- ゲームエリア外(余白・ヘッダーなど)は意図どおりにスクロールするか確認する
iOSとAndroidはタッチイベントの細かい挙動が異なるため、どちらか一方だけでなく両方で確認する習慣をつけておくと、後から別の端末で問題が出るリスクを減らせます。
次の行動
まずゲームのプレイ領域に touch-action: none をCSSで追加し、実機で動作を確認してみてください。それでもスクロールが残る場合は、touchmove と touchstart のイベントリスナーに { passive: false } が付いているかを確認し、e.preventDefault() を呼んでいるかを見直してください。修正したゲームはHTMLファイルやZIPにまとめてアソバース(https://aso-verse.com)に投稿し、スマホからの動作を公開環境で確かめることができます。
参考資料
- MDN Web Docs「EventTarget.addEventListener()」
- MDN Web Docs「touch-action」
- MDN Web Docs「Touch events」
- Web Fundamentals「Passive event listeners」(Google Developers)

