この記事に書かれている現象は iOS 10.3 にて改修されました。
以下の内容は過去の事象としてご参照ください
何を言っているんだおまえは、と突っ込みたくなる方もいらっしゃるかと思いますが、ありのままに今起こったことを話すぜ…!
まずは動画をご覧ください。
再現条件とか
以下の環境において端末を横位置にした上で画面をスクロールさせてタブバーを表示させると、{ position: fixed }
で配置した要素が正常に機能しなくなります。
上記の動画ではメニューアイコンに張ったクリックイベントハンドラーが動作していないわけです。
- iPhone 6s Plus
- iOS 8.4 / 9.1 / 9.2 / 9.3 / 10.0 / 10.1 / 10.2
- Safari
iOS用の Mobile Safari では端末を横位置にしたときにのみタブバーが表示される仕様となっています。
ただしこの仕様は iPhone 6 Plus と iPhone 6s Plus の大画面版iPhoneでのみ採用されており、Plus系でないiPhoneやタブバーが常に表示されているiPadでは同様の不具合は発生しません。
(実機/シミュレータ双方で確認済み)
追記 (2017/3/28 11:01)
日本時間2017年3月28日にリリースされた iOS 10.3 にて動作確認をしたところ、本現象が 再現されません でした。
最初にこの現象に気付いてから早1年半…。やっと…やっと改修されたよ…!!
追記 (2016/9/14 10:49)
満を持してリリースされた iOS 10.0 にて動作確認をしたところ、残念ながら今回もバグは修正されませんでした。
やはり人類にfixedは早すぎたのか…?
※ 合わせて記事のタイトルを変更
追記 (2016/3/22 15:17)
3ヶ月ぶりの追記が残念なお知らせとなってしまいました。
iOS 9.3 にて動作確認をしたところ、未だバグの解消には至っていません。
報告したのになー…。
追記 (2015/12/9 10:40)
公開されたばかりの iOS 9.2 にアップデートして試したところ、引き続き本現象が再現しました。
残念ながらまだしばらく解消されないようです…。
追記 (2015/11/18 17:01)
本現象が iOS 8 の iPhone 6 Plus (シミュレーター)でも発生することを確認しました。
ググってもそれらしい情報がヒットしないのは、横位置にする人がほとんどいないからでしょうか?
なぜ機能しないのか
端末を横位置にしてタブバーを表示させた上で、機能しないfixedな要素をインスペクターで拾ってみたところおかしなことになっていました。
なんか画面の描画と計算上の位置(青と黄色でマスクされているところ)とがずれまくってるんですけど…。
挙動から想像するに、タブバーが表示された瞬間にDOMの座標情報の更新が止まってしまってるように見えます。
また上記スクショの状態でリロードをかけるても、やはりfixedな要素は機能してくれません。
その状態で試しにfixedな要素のClientRectを取得してみたところ謎の値が返ってきました。
{
bottom: -1509,
height: 56,
left: 928,
right: 984,
top: -1565,
width: 56
}
heightが画面の高さよりも小さいのに、topとbottomがどちらもマイナスになっており明らかに変です。
解決方法はあるのか?
先に結論を言っておくと、現時点で合理的な解決方法はなく iOSのアップデートを待つ しか無いです。
以下いくつかの方法を試してみたのでサンプルとあわせてご確認いただければと思います。
とりあえず現象の確認用に何も対策を施していないサンプルを用意しました。
CodePen - Oh fixed! (normal)
GPUハック
サンプル: CodePen - Oh fixed! (GPU hack)
とりあえず、おなじみの方法を試してみました。
fixedな要素に対して以下のスタイルを適用しています。
.gpu-hack {
transform: translate3d(0, 0, 0);
}
結果は効果無し!
Re-drawハック
サンプル: CodePen - Oh fixed! (Re-draw hack)
つづいて、強制的に再描画をさせるハックを仕掛けてみました。
fixedな要素に対して以下のスタイルを適用しています。
@keyframes re-draw {
from { min-width: 1px; }
to { min-width: 2px; }
}
.re-draw-hack {
animation: re-draw 1s infinite;
}
こちらも効果無し!
JSでtransformする
サンプル: CodePen - Oh fixed! (Transform with JS)
GPUハックにしてもRe-drawハックにしても、画面の描画が追いついていないような場合に有効なハックなので、今回のように画面描画は正常なのに内部的な座標系が更新されない、という場合に効果が無いのはしかたないのかなと思います。
じゃあどうすれば良いんだって感じですけど、かつてiOSもAndroidもまだ{ position: fixed }
が実装されていなかった時代のやり方で対応するのが以下の方法。
.sim-fixed {
position: absolute;
/** 以下略 **/
}
$(function() {
var $fixed = $('.js-fixed');
$(window).on('scroll touchmove', null, function() {
$fixed.css('transform', 'translateY(' + window.scrollY + 'px)')
});
});
要素自体はabsoluteで配置しておいて、画面の移動量に応じてJSでtransformさせる。
いやー哀しみを感じますね。
宇宙の 法則が 乱れる!
iOS/Androidに{ position: fixed }
が実装された当初はバグだらけで使い物にならなかったのが最近はようやく安定してきて、ヘッダーなどで普通に使用しているサイトは世界中に数多あるはずです。
今回のこのバグはそれら 全てで発生する はずですので、みなさんも心当たりのあるサイトを調べてみてください。
まあ調べたところで、じゃあどうすんだよって話ですけどw…。
2015年も終盤にさしかかってまだバグるか{ position: fixed }
!
このバグの一日も早い解消を願って、本件はAppleに報告いたします。