スマホやタブレットでピンチインアウトによる画面の拡大縮小を禁止させたいことがあったのでメモ。

「ピンチインアウト禁止」はブラウザの仕様変更のせいで、その手段には歴史がある。

黎明期はviewport指定

昔はviewportにuser-scalable=noを指定していれば拡大縮小を禁止できていた。

<meta
  name="viewport"
  content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"
>

ただ、拡大縮小の可否をユーザーに委ねられないのはユーザービリティ的にいかがなものなのか?という議論があったのを覚えている。

2016年以降はpreventDefault()

iOS10ぐらいからviewportによる制御が効かなくなってきた。

ユーザービリティの倫理観はさておき、とにかく拡大縮小させたくないという状況はやはり存在する。
しかたないので javascript で強引に禁止させるしかない。
touchstart, touchmoveイベントを監視して preventDefault() を実行すれば禁止できる。

document.body.addEventListener("touchstart", function(e){
  e.preventDefault();
});
document.body.addEventListener("touchmove", function(e){
  e.preventDefault();
});

iOS11以降でaddEventListenerの仕様変更

addEventListenerの第3引数はオブジェクトを受け取ることができる。
https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener
optionsにpassiveというのがあり、これがtrueだと第2引数の関数内でpreventDefault()が利用できなくなるらしい。
それはそれでいいのだけど、iOS11.3やchrome55以降のブラウザはbodyなどのtouchstartやtouchmoveのaddEventListenerするとき、passiveのデフォルト値がtrueに変更されたらしい。
なので、passiveにfalseを指定する必要がある。

document.body.addEventListener("touchstart", function(e){
  e.preventDefault();
}, {passive: false});
document.body.addEventListener("touchmove", function(e){
  e.preventDefault();
}, {passive: false});

フォームに入力できない、ボタンが押せない

cssのUIキットの中にはtouchstartに連動して動作するフォームを提供するものがある。
(onsenUI、Vuetify.jsなど)
上の方法でpreventDefault()してしまうとテキストエリアがクリックできなかったりボタンが押せなかったりしてしまう。
拡大縮小は禁止したいが、フォームは生かしたい。

ピンチインアウトは2本指で動作するので、そこで条件分岐させる。

document.body.addEventListener("touchstart", function(e){
  if (e.touches && e.touches.length > 1) {
    e.preventDefault();
  }
}, {passive: false});
document.body.addEventListener("touchmove", function(e){
  if (e.touches && e.touches.length > 1) {
    e.preventDefault();
  }
}, {passive: false});