Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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 をするのは処理の最後に書きましょう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした