在移动端开发中,fixed 元素和 input 输入框在同一页面是常有的事。但是 在键盘配唤起
(也就是输入框获取焦点)的情况下,会出现一些莫名其妙的 Bug,尤其在 ios 中。这里记录下我遇到的一些问题以及解决方案。
假设有这样一个页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <body> <header> ... </header>
<main> ... </main>
<footer> <input type="text" value=""> <button class="btn"></button> </footer> </body>
|
对应的页面样式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| header{ position: fixed; height: 44px; left:0; right:0; top:0; } footer { position: fixed; height: 40px; left: 0; right:0; bottom:0; } main{ min-height: 100%; margin-top:44px; margin-bottom: 40px; }
|
这样一个常规的 fixed 布局就完成了,目前事完全正常的。
但是底部输入框唤起键盘以后,再次滑动页面,就会发现 fixed 布局元素跟随页面滚动起来了???fixed 属性失效了!!!
怎么回事啊,老弟???
原来是在键盘被唤起后,fixed 元素失效,换句话说就是元素不能浮动,与 absolute 一致,而当页面高度超出一屏时,失效的 fixed 元素会随页面滚动一起滚动。
既然如此,那让页面整体高度固定且不能滚动,就算 fixed 属性还是失效,也不就会出现上面的问题了。
OK,照着这个方法,fixed 元素的父级不能滚动,就要将原本的滚动区域移到一个固定高度的元素里,还按照上面那个布局来看,就是将滚动内容移到 main 内部,再给 main 一个定高,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <body> <header> ... </header>
<main> <div class="contene"> ... </div> </main>
<footer> <input type="text" value=""> <button class="btn"></button> </footer> </body>
|
1 2 3 4 5 6 7 8
| 其他布局不变
main { position: absolute; top: 44px; bottom: 40px; overflow-y: scroll }
|
看上去时解决问题了,在手机上实际测试一下,也是没问题的,但是发现滚动十分不流畅,手指
松开后,滚动立即停止。加上下面的属性就可以恢复弹性滚动。
1
| -webkit-overflow-scrolling: touch
|
再来看下,嗯~ o( ̄ ▽  ̄)o,立刻纵享丝滑。
好的,到此为止,fixed 布局在 ios 中的 bug 总算解决了。当然,还可以借助 isScroll.js 解决这个问题。
此外,在 ios 中使用第三方输入法,输入法在被唤起时经常盖住输入框,只有在输入文字
后,输入框才会浮出。目前没有发现什么好方法可以解决这个问题,算是 ios 的一个坑。
混合 App 下的布局
在 app 里唤起键盘后,键盘同样也占据一定的屏幕高度,结果就会导致整个页面的高度被挤压。由于 fixed 元素是相对于 body 定位的,这样会出现 fixed 元素盖住输入框的情况(在一些小屏手机上更严重)。
例如下面这个示例:
1 2 3 4 5 6
| <body class="layou-fixed> <form class="form"> <input type="text"> </form> <button class="btn">提交</button> </body>
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| .layout-fixed { ... }
.form { ... }
.btn { position: fixed; bottom: 30px; ... }
|
编译打包成 App,在手机上测试一下,就出现上述问题。
由于是键盘导致的页面高度不够,所以尽量不要用 fixed 布局。这里可以使用 css 最新的 flex 布局,完美的解决了页面高度不够的问题。
1 2 3 4 5 6 7 8 9 10 11
| .layout-fixed{ display: flex; flex-direction: column; justify-content: center; } .form{ flex: 1; } .btn{ margin-bottom: 30px; }
|
这样当键盘唤起造成页面高度不够时,页面会变成可滚动的,而整体布局不会变化,避开了 fixed 布局的 bug。
其他
有时候输入框获取焦点(focus)后,会出现键盘盖住输入框的情况,这时候可以尝试 input 元素的 scrolltoView 进行修复。
页面滚动到上下边缘的时候,一旦继续拖拽会将整个 view 一起拖拽走,导致页面露底。
为了防止页面露底,可以在页面拖拽到页面边缘的时候,通过判断拖拽方向以及是否为边缘来阻止 touchmove 事件,防止页面继续拖拽。
以上面 ios 下的 fixed 布局为例,一下代码仅供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| // 防止内容区域滚到底后引起页面整体的滚动 var content = document.querySelector('main'); var startY;
content.addEventListener('touchstart', function (e) { startY = e.touches[0].clientY; });
content.addEventListener('touchmove', function (e) { // 高位表示向上滚动 // 底位表示向下滚动 // 1容许 0禁止 var status = '11'; var ele = this;
var currentY = e.touches[0].clientY;
if (ele.scrollTop === 0) { // 如果内容小于容器则同时禁止上下滚动 status = ele.offsetHeight >= ele.scrollHeight ? '00' : '01'; } else if (ele.scrollTop + ele.offsetHeight >= ele.scrollHeight) { // 已经滚到底部了只能向上滚动 status = '10'; }
if (status != '11') { // 判断当前的滚动方向 var direction = currentY - startY > 0 ? '10' : '01'; // 操作方向和当前允许状态求与运算,运算结果为0,就说明不允许该方向滚动,则禁止默认事件,阻止滚动 if (!(parseInt(status, 2) & parseInt(direction, 2))) { stopEvent(e); } } });
|
参考
web 移动端 Fixed 布局解决 iOS 下的 Fixed+Input BUG 现象