タイトルが少し分かりにくいですが、display:flex
の中でposition:absolute
が指定された要素の初期位置がどこになるか、という話です。
経緯
ポップアップメニューのコンポーネントを実装していて、フレックスボックスと絶対位置指定(position:absolute
)を利用してポップアップメニューを中央揃えさせようとしましたが、SafariとIE11では期待通りの表示になりませんでした。
試してみた方法は、ポップアップのメニューとボタンを包むラッパーをフレックスボックスにして、justify-content:center
で中央寄せし、メニューの高さ方向だけ絶対位置指定する方法。
HTMLとCSSは必要な箇所以外を省略しています。
ただし前述の通り、SafariとIEでは中央揃えにならなかったので、そもそもフレックスボックス内で絶対位置指定された要素の初期位置(静的位置)って仕様ではどうなってるんだろうと調べました。
現在の仕様について
まずは現在の仕様から確認していきます。
CSS Flexible Box Layout Module Level 1 #4.1. Absolutely-Positioned Flex Childrenでは、次のように定められています。続く日本語訳は私のなんとなくの訳です。
フレックスコンテナ内の絶対位置指定された子(要素)はフローから外れているのでフレックスレイアウトに参加しません。
フレックスコンテナ内の絶対位置指定された子(要素)の静的位置は、その子とフレックスコンテナが使用されているサイズの固定サイズボックスであると仮定し、さらにその子(要素)がフレックスコンテナの唯一のフレックスアイテムであった場合の位置となります。この目的のために、align-self:auto
の値はstart
と同じように扱われます。
大事なのは静的位置は(中略)子要素がフレックスコンテナの唯一のフレックスアイテムであった場合の位置の箇所のようですね。
実装してみてブラウザの挙動を確認
ということで、乱暴に言うと絶対位置指定された(けどtop
やleft
などが指定されてない)時の位置
=
その子要素がフレックスコンテナの唯一のフレックスアイテムであった場合の位置となるはずですので、それぞれ実装して確認してみます。
See the Pen Absolutely-Positioned Flex Children by masuP9 (@masuP9) on CodePen.
ChromeやFirefox、Edge(の2017/06/05時点での最新版)では、仕様どおりに中央に表示されていますが... おや、Safari と IE の様子が...
- Chromeでの表示例
- Safari バージョン10.1.1 (12603.2.4)での表示例
- IE11での表示例
フレックスボックスの過去の仕様を遡ってみると
Safariについては webkit.orgにてバグ報告されているみたいでした。156798 - Abspos flex item static position。
バグ報告の中身を見ていると、仕様がアップデートされたが実装がアップデートされていない。という文言がありましたので、古い仕様を遡ってみてみました。すると2012年9月18日に出た仕様では、次に続く同一フレックスライン上のフレックスアイテムがあれば、そのアイテムの外側のメインスタートエッジが静的位置だとされているので、まさにIEやSafariの挙動は古い仕様における正しい位置でした。
IEにはもう期待しないとして、Safariには新しい仕様に対応して欲しいですね...
- 2017/06/07 追記: Safari Technology Preview Release 31 (Safari 10.2, WebKit 12604.1.23.0.1)では、現在の仕様に対応しているようです。
- 2017-09-28 追記: Safari バージョン11.0 (12604.1.38.1.7) にて、現在の仕様に対応していることを確認しました。
まとめ
まとめるとフレックスコンテナ内で絶対位置指定された要素の静的位置は、現在の仕様ではそれが唯一のフレックスアイテムだとしたときの位置となるはずですが、SafariとIEは古い仕様を元にした実装のままとなっているのか、期待した動作にならないことが分かりました。
当初の目的であった可変サイズのボタンとポップアップメニューの中央揃え、どなたかいい方法があれば教えてください。
参考までにYoutubeやFacebookのポップアップの実装を見てみると、コンポーネントとしてまとめるのではなく、ポップアップで出てくるメニューやツールチップは別のDOMとして生成しておいて、実行されたボタンの位置にJavaScriptでゴリゴリ合わせる、というような実装になってるぽく、うーん、そこまでやるのもどうかだなあと考えているところです。