LoginSignup
115

More than 3 years have passed since last update.

iOS10のSafariでuser-scalable=no が効かなくズームがされる問題への対策

Last updated at Posted at 2016-09-27

iOS10から、Safariで viewportuser-scalable=no が効かなくなりました。
ブラウザゲームなどを開発してる場合、ダブルタップで拡大されてしまうためクリティカルな問題になります。
Appleへの恨みを込めつつ、対策方法を調べました。

なお、
参考サイト: http://stackoverflow.com/questions/37808180/disable-viewport-zooming-ios-10-safari
に近いですが、このままではうまくいかなかったので、ちょっとアレンジしています。

複数指で拡大縮小が出来てしまうのを防ぐ

touchstart のイベントを見て防ぎます。
なお、子要素で、stopPropagation()をしてる場合、バブリングフェーズでpreventDefault()をしても効果がなくなるため、意図的にキャプチャフェーズでpreventDefault()をしています。
addEventListenerの第三引数がtrueになります。)


document.addEventListener('touchstart', event => {
  if (event.touches.length > 1) {
    event.preventDefault();
  }
}, true);

ダブルタップを防ぐ

touchend のイベントを見て防ぎます。
こちらも同様に、意図的にキャプチャフェーズでpreventDefault()をしています。
addEventListenerの第三引数がtrueになります。)

なお、いくつかのサンプルサイトでは0.3秒の判定で対策されてましたが、私の環境では0.45秒は必要でした。
バッファも考慮し、0.5秒(500ms)としました。
時間測定もあえてDateTimeではなく、performance.now()を使っています。


let lastTouch = 0;
document.addEventListener('touchend', event => {
  const now = window.performance.now();
  if (now - lastTouch <= 500) {
    event.preventDefault();
  }
  lastTouch = now;
}, true);

もしくは、setTimeoutを使ってもいいです。


let flag = false;
document.addEventListener('touchend', event => {
  if (flag) {
    event.preventDefault();
  } else {
    flag = true;
    setTimeout(() => {
      flag = false;
    }, 500);
  }
}, true);

フォームにカーソル移動時に拡大される

この問題はuser-scalable=noとは関係ありませんでした。
情報提供を頂いた harapeko_wktk さんありがとうございました。

iOS10未満はviewportにinitial-scale=1, minimum-scale=1, maximum-scale=1でも拡大防止できていましたが、できなくなったようです。
iOS10からはfont-sizeを16px以上にするかuser-scalable=noが必要という結果でした。

16px未満のフォントサイズのときに拡大がされてしまうようになりました。
こちらに関しては、16px以上にfont-sizeを指定する事で拡大を阻止出来ます。

16px未満のまま防止する方法はもしかするとあるかもしれませんが、Safariの仕様変更の意図を考えるとあまり16px以下に拘る必要性は無いかなと個人的には解釈しています。

困った問題

さて、これで基本的には解決するはずですが、解決しないこともあります。

  • 時々ダブルタップをすると、lastTouchの値が2000を超え、上記処理では拡大を防ぐことができない。
  • 時々ダブルタップをすると、touchendイベントが1回しかこないのにダブルタップ扱いになる。

もうこれ完全にSafariのバグだろと思いつつ、
私はいろいろ試行錯誤したあげく、最終的に以下の手段を取りました。

(最終手段) 拡大されたくない要素に対して、touchendイベントをキャンセルする

element.addEventListener('touchend', event => {
  event.preventDefault();
}, false);

これをすると、いくら連打してもダブルタップしてもズームはされなくなりました。
但し、touchendイベント自体がキャンセルされてしまうため、この後にaddEventListener でイベントが登録できなくなります。
preventDefault をするのは処理の最後に書きましょう。

(追記)cssで対応する

コメント欄で教えていただきました。これがもっとも対応簡単だと思います。

touch-action: manipulation;

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
115