iOS下的Fixed元素和输入框唤起的情况下布局方案

简介:以下解决方案仅限于固定布局有输入框的情况。在 iOS Safari 5-7 中,位置:固定将移动到窗口中心,焦点事件在子文本输入字段上。在 Android 4.0-4.3 位置:固定在 iframe 内会导致意外行为.原创:Web Mobile Fixed Layout Solutions 移动业务开发,在iOS下,经常会出现固定元素和输入框(输入元素)同时存在的情况。但是,当固定元素被软键盘唤起时,就会出现很多莫名其妙的问题。本文提供了一个简单的带有输入框的固定布局方案。 iOS下的Fixed + Input BUG现象我们先举个栗子,最直观的解释一下BUG现象。对于常规的固定布局,可以采用如下布局(以下代码仅作说明):

<body class="layout-fixed">
    
    <header>
        
    </header>
    
    
    <main>
        
    </main>
    
    
    <footer>
        <input type="text" placeholder="Footer..."/>
        <button class="submit">提交</button>
    </footer>
</body>

对应的样式如下:

header, footer, main {
    display: block;
}
header {
    position: fixed;
    height: 50px;
    left: 0;
    right: 0;
    top: 0;
}
footer {
    position: fixed;
    height: 34px;
    left: 0;
    right: 0;
    bottom: 0;
}
main {
    margin-top: 50px;
    margin-bottom: 34px;
    height: 2000px
}

然后它看起来像这样。拖动页面时,页眉和页脚已经定位到相应位置,视觉上没有问题。

固定定位

但是问题来了!如果底部输入框的软键盘被唤醒,再次滑动页面,会看到如下图:

我们看到固定定位的元素随着页面滚动……固定属性不起作用!为什么是这样?简单说明: > 软键盘唤醒后,页面的固定元素会失效(即不能浮动,也可以理解为绝对定位),所以当页面超过一屏滚动时,无效的固定元素将跟随滚动。这是 iOS 上固定元素和输入字段的错误。不限于type=text的输入框,任何软键盘(如时间日期选择、选择选择等)被唤醒,都会遇到同样的问题。虽然 isScroll.js 可以很好的解决固定定位和滚动的问题,但是我们尽量尝试了一种不依赖第三方库的布局方案来简化实现。这是一个参考指南。解决方法:由于iOS下软键盘调出后页面的固定元素会失效,导致随页面滚动fixed 定位内容可滑动,那么如果页面不滚动太久,那么即使固定元素失效,也无法跟随页面滚动。不会出现上述问题。那么按照这个思路,如果固定元素的父级不滚动,而是将原body的滚动区域移到main里面,而header和footer的样式保持不变,代码为如下:

<body class="layout-scroll-fixed">
    
    <header>
        
    </header>
    
    
    <main>
        <div class="content">
        
        </div>
    </main>
    
    
    <footer>
        <input type="text" placeholder="Footer..."/>
        <button class="submit">提交</button>
    </footer>
</body>

header, footer, main {
    display: block;
}
header {
    position: fixed;
    height: 50px;
    left: 0;
    right: 0;
    top: 0;
}
footer {
    position: fixed;
    height: 34px;
    left: 0;
    right: 0;
    bottom: 0;
}
main {
    /* main绝对定位,进行内部滚动 */
    position: absolute;
    top: 50px;
    bottom: 34px;
    /* 使之可以滚动 */
    overflow-y: scroll;
}
main .content {
    height: 2000px;
}

再看一遍:

固定定位

在原输入法下,固定元素可以定位在页面上的正确位置。滚动页面的时候,因为main里面的div是滚动的,所以footer不随页面滚动。以上看似解决了问题,但如果你实际在手机上测试,你会发现主元素中的滚动很不流畅。滑动手指松开后,滚动立即停止,失去了原本流畅的滚动特性。百度了一下弹性滚动问题,发现在webkit中,以下属性可以恢复弹性滚动。 -webkit-溢出滚动:触摸;在主元素上加上这个属性,嗯,丝滑又回来了!

main {
    /* main绝对定位,进行内部滚动 */
    position: absolute;
    top: 50px;
    bottom: 34px;
    /* 使之可以滚动 */
    overflow-y: scroll;
    /* 增加该属性,可以增加弹性 */
    -webkit-overflow-scrolling: touch;
}

另外,这里的页眉和页脚使用固定定位。如果你认为旧的 iOS 系统不支持固定元素fixed 定位内容可滑动,你完全可以用 absolute 替换 fixed 。测试后效果是一样的。至此,一个不依赖第三方库的固定布局就完成了。 Android下的布局讲了iOS,我们简单说一下Android下的布局。在Android2.3+中,由于不支持overflow-scrolling,在某些浏览器中滚动可能不流畅。但是发现body上的滚动还是很流畅的,所以使用iOS中出现问题的第一个固定定位布局就可以了。如果需要考虑Android2.3以下的系统,由于不支持固定元素,还是需要考虑使用isScroll.js实现内部滚动。其实在固定和输入框的问题上,基本思路是: > 由于固定在软键盘被唤醒后会失效,所以在页面可以滚动的情况下会随着页面滚动。因此,如果页面不能滚动,即使固定元素失效,也不会滚动,不会出现bug。所以我们可以考虑解决这方面的问题。其他一些细节处理的很详细。其实还有很多需要注意的地方。挑几个实际遇到的问题:有时候输入框对焦后,软键盘会挡住输入框。当你可以尝试输入元素的 scrollIntoView 来修复它。在iOS下使用第三方输入法时,输入法在被唤醒时往往会覆盖输入框,输入框只有在输入一段文字后才会出现。目前,我不知道有什么好的方法可以在输入框被唤起时让它正确显示。这暂时是iOS下的一个坑。一些第三方浏览器底部的工具栏浮动在页面顶部,所以底部固定定位会被工具栏挡住。解决方案也比较简单粗暴——适配不同的浏览器,调整固定元素与底部的距离。最好禁用页眉和页脚元素的 touchmove 事件,以防止在它上面滚动触发某些浏览器的全屏模式切换,导致顶部地址栏和底部工具栏遮挡页眉和页脚元素。当页面滚动到上下边缘时,如果继续拖动,整个View会一起被拖走,导致页面“底部曝光”。

固定定位

为了防止页面底部暴露,当页面拖动到边缘时,可以通过判断拖动方向以及是否是边缘来阻止touchmove事件,防止页面继续被拖。以上面的 layout-scroll-fixed 布局为例,给出一段代码供参考:

// 防止内容区域滚到底后引起页面整体的滚动
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);
        }
    }
});

© 版权声明
THE END
喜欢就支持一下吧
点赞187赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容