9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Safari特有のモーダルウィンドウのバグと戦った話

Last updated at Posted at 2019-12-13

Ateam Lifestyle Advent Calendar 2019の14日目は
新卒2年目 株式会社エイチームライフスタイル 名古屋開発部の@kiitanが担当します。

普段はバックエンド(rails)の開発をメインでやっています。
Reactを書き始めてちょうど1年位になります。

はじめに

画面遷移なしでユーザに選択肢を表示したい。
こんな時よく使われるモーダルウィンドウですよね。
ただSafariにいくつかの罠がありそれを踏み抜いたエンジニアの戦いの記録である。

背面のスクロールが止まらない

ios12のSafariでは、モーダルウィンドウ内のスクロールが終わったあと背面がスクロールされてしまう。

検索するとよく出てくるのが <body>要素にoverflow: hiddenをつける対策です。
しかしこれだけは裏側のスクロールを止めることはできませんでした。

ここに更にposition: fixedをつける必要があります。
まとめるとbody要素には下記のcssをつける必要があります。

body {
  overflow: hidden;
  position: fixed;
}

しかしposition: fixedをつけるとモーダルを開いた時に最上部まで戻ってしまう 。
なのでモーダルウィンドウを開いた時点の位置をとりそこまでスクロールする必要がでてきます。

import React, { useState } from 'react' 
import ReactDOM from 'react-dom' 
import ReactModal from 'react-modal' 
import * as ReactScroll from 'react-scroll' 

import './styles.css' 

const scrollTopPx = () => {
  return Math.max(
    window.pageYOffset,
    document.documentElement.scrollTop,
    document.body.scrollTop
  ) 
} 

const App = () => {
  const [isModalOpen, toggleModal] = useState(false) 
  const [scrollTop, setScrollTo] = useState(0) 

  return (
    <div className={`App ${isModalOpen ? 'modal-open' : ''}`}>
      {[...Array(30).keys()].map(n => (
        <p key={n}>{n}背面</p>
      ))}
      <button
        onClick={() => {
          toggleModal(!isModalOpen) 
          setScrollTo(scrollTopPx()) 
        }}
      >
        Toggle Modal
      </button>
      <ReactModal isOpen={isModalOpen}>
        {[...Array(40).keys()].map(n => (
          <p key={n}>{n}モーダル内</p>
        ))}
        <button
          onClick={() => {
            toggleModal(false) 
            ReactScroll.scrollTo(scrollTop) 
          }}
        >
          toggle
        </button>
      </ReactModal>
    </div>
  ) 
} 

const rootElement = document.getElementById('root') 
ReactDOM.render(<App />, rootElement) 

動作はこちらで見ることができます
https://codesandbox.io/s/hooks-modal-n3j3f

まとめ

最後まで読んでいただきありがとうございました。
ブラウザ固有の問題にハマったことがなかったので良い経験になりました。

Ateam Lifestyle Advent Calendar 2019の15日目は、@turkeyzawaがお送りします!
フロントエンドに強いエンジニアなので僕も明日の記事公開が楽しみです。


"挑戦"を大事にするエイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/

9
1
0

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
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?