今天前同事李某找我咨询 Hybrid 开发的问题,想起来大前天搞这个问题搞了一天,赶紧记下来,省得忘记。
先说需求。东家让我做个日历组件,在手机 Web 上用。组件的样式是这样的,很多地方都可以见到,比如南航国航的客户端。
看起来并不复杂,事实上也是,基本上顺顺利利的开发完成,准备交付。这里有个伏笔,开发中我按老习惯,使用桌面 Chrome,和实际生产环境不太一样。不过我自然要去真机上测试,结果一测问题就出来了。
因为组件需要全屏展示,所以我设置了如下的CSS:
.date-picker {
position:fixed;
top:0;
left:0;
right:0;
bottom:0;
background-color: white;
z-index:1024;
}
同时,对原本的 <input name="date">
,我给它加上 readonly
,避免弹出虚拟键盘。理论上,这样的就可以了。但实测时,不滚屏的时候,组件弹出时尺寸是准确的,盖满全屏;然则一旦滚屏,组件就会占据从页面最上方到当前最下面这截位置。大约相当于 position:absolte;top:0
的效果。
如图,可以看到组件占据了全屏,但实际是从页面最上面开始的,定位有问题。用桌面 Safari 调试也可以看出来它的高度是 968,远大于正常的 667。
这很诡异,上下左右全为0,是上古巨兽 IE6 都支持的做法。iOS Safari 虽然 Bug 多多,不应该连这个都有毛病啊。以 ios safari position fixed
为关键词 Google 之,结果 iOS Safari 历史不清白,当年 iPhone 刚出的时候的确有定位问题,于是虽有满屏的结果,但都不适用。
然后我想到找其它库,比如 Bootstrap,它的 Modal 组件也是类似的效果。但是怎么测都正常,于是我只好一个样式一个样式修改,仍然没有结果。
时间慢慢流逝,转眼已经凌晨2点了,就在我几欲放弃之际,突然发现,虽然组件弹出的时候定位有问题,但只要我点掉下面的完成,定位就会立刻恢复正常。
注意,就是那个“完成”。
问题至此已经明朗:在 iOS Safari 里,即使 <input>
设置了 readonly
,它仍然可以获取输入焦点。获取输入焦点之后,虽然没有弹出虚拟键盘,但仍然是待输入状态。
此时页面各种交互都是正常工作的,比如点击、滚屏。唯独 position:fixed
定位有问题。点击“完成”离开输入状态,Safari 自动刷新页面元素,定位就正常了。
于是我在组件弹出后,自动 input.blur()
,使其失去焦点,组件的尺寸便正常无误了。
总结
移动端 Web 开发总有各种各样稀奇古怪的问题。有些好解决,有些不好解决,比如这个问题,很难定位:
- 历史不清白,搜也搜不到
- 组件要求全屏,需要避免虚拟键盘,所以会改变默认行为
- 其它情况下都是好的
我能想到的方案,就是想办法,用所有能用的工具,排除掉所有其它问题,最终还是能搞出来的。
欢迎吐槽,共同进步