Jan11

「Stay hungry. Stay foolish.」


  • 首页

  • 归档

  • 标签

position:sticky 踩坑记录

发表于 2016-08-25   |  

position:sticky 踩坑记录

背景

最近在移动端需要实现导航栏的粘性布局,看到原型的时候也就想了下怎么实现。不外乎是通过js监听window的scroll事件,当需要固定的元素滚动到窗口顶部时,把元素的position属性设置为fixed,否则,取消fixed,简单代码如下:

var nav = document.querySelector('.navBar');
function onScroll(e) {
window.scrollY >= nav.offsetTop ? nav.classList.add('fixed') : nav.classList.remove('fixed');
}
window.addEventListener('scroll', onScroll);

开发的时候一直在用Chrome的模拟器,滑动起来很顺畅,后来测试的时候用真机发现粘顶效果很不好。ios滑动的时候会暂停所有的js执行,等待滚动完成之后才会执行,所以上面代码的监听并没有在滑动时响应。安卓机下面相对好一点。但是作为一个水果机,竟然有如此不优雅的展现方式,怎么对得起乔帮主的在天之灵啊!

既然浏览器自带滚动条不适用,就尝试用类似iscroll插件模拟滚动条,但是考量了一下,滚动页面需要上拉加载更多,需要对整页进行iscroll,损耗性能不说还要重新实现iscroll(或者引入插件)。所以准备使用position的新属性 Sticky。

##Sticky positioning
sticky布局是一个类似relative和fixed的混合布局。在视窗范围内,元素会以relative布局显示,当超过阀值时会以fixed布局显示。参照

例如:

#one { position: sticky; top: 10px; }

当页面滚动到sticky在文档流中位置元素离视窗顶端的距离<=10px时,sticky元素就开始固定了,当sticky元素在文档流中位置离viewport顶端的距离>10px时,元素就不再固定。

至此,sticky属性完全符合我们的要求。但是很遗憾,作为一个实验属性,有很多浏览器并没有实现该属性。大家看看下图:

各浏览器兼容情况

chrome下全红,安卓下也基本没有实现。庆幸的是,我现在只需要照顾到移动端方面,ios6+都已经支持了该属性,安卓下就使用fixed属性好了。

// 判断是否支持sticky属性
function isSupportSticky() {
var prefixTestList = ['', '-webkit-', '-ms-', '-moz-', '-o-'];
var stickyText = '';
for (var i = 0; i < prefixTestList.length; i++ ) {
stickyText += 'position:' + prefixTestList[i] + 'sticky;';
}
// 创建一个dom来检查
var div = document.createElement('div');
var body = document.body;
div.style.cssText = 'display:none;' + stickyText;
body.appendChild(div);
var isSupport = /sticky/i.test(window.getComputedStyle(div).position);
body.removeChild(div);
div = null;
return isSupport;
}

##生效条件
按照上面的方法设置好,很大程度上还是不会实现粘顶的效果,查了很多资料,都没有给出很好的原因,然后我就硬着头皮去看官方文档。发现sticky属性是有生效条件的。

  • 任何一级父节点都不能overflow为非visible,否则都不会生效
  • 设置了postion:sticky的元素要生效必须要至少设置top,bottom,left,right中的一个属性,并且同时设置时,top>bottom,left>right。

参考:
CSS Positioned Layout Module Level 3
CSS “position: sticky” – Introduction and Polyfills

移动端虚拟键盘与fixed

发表于 2015-03-16   |   分类于 Blog   |  

#前言
前段日子,运营一哥们跟我说,我们之前上线的xx商城,移动端在部分苹果手机上打字的时候会出现很诡异的事情。我研究了几天,觉得需要贴出来备忘一下。


#问题描述
当出现虚拟键盘时,文本(类)框获得焦点时,原来的fixed就失去效果了。移动端有一个footer是用fixed定位的。下图标红的部分:
免税店
但是如果弹出虚拟键盘就会失去效果,原图我现在没有,就在网上照了一张,大家凑合着看:
弹出虚拟键盘

#解决之路
本来我以为所有手机都会出现这种问题,但是经过多方排查,只有ios系统7.2的版本会出现这个问题。其他android和ios其他版本均不会出现。之后查了些资料,最后还是解决了问题。<鼓掌>虽然有点geek。

在移动设备上,如果文本框在上方,点击不会有什么问题;在设备的最下面的话,就有所不同了,整个块会上移,以将input区域显示出来;
首先以下有些问题是我不可处理的:

  1. 虚拟键盘的出现对页面来说是不可知的,没有键盘出现事件,没有办法获取键盘高度
  2. 键盘是浮在了viewport上,表面上不会对dom产生“任何”影响,但是这个时候一些定位元素的表现却变得诡异

一般大公司的处理方法是:移动端不采用fixed属性。在设计环节就杜绝问题的出现。但是这显然不适合我们,产品都已经上线了啊。
然后我起初的构思是从根源着手,深入研究两个知识点:

  1. viewport的原理
  2. ios虚拟键盘的原理

就我手里现有资源和时间来说,都不能完成,所以只能先从应用层面解决问题。
应用层面解决方案
我们想到这么一个场景,如果我们能监控到键盘的行为,如果能的话,我们便可以
1.键盘弹出时候将fixed元素设置为static2.键盘消失时候将fixed元素设置为fixed

这个方案好恶心啊,我当时想到的时候也被这个方案吓了一跳,但是或许这是现阶段最好的解决方案了。
我的做法就是:监控dom变化!
监控键盘
监控的方式其实筛选下来也不过两种:

  • 时钟setInterval不停监控
  • 系统级别的监控,比如键盘出现时候通知window一个事件,但是很遗憾现在还没有这个事件,但是这个事件等于
    input类元素获取焦点 == 弹出虚拟键盘input类元素失去焦点 == 收起虚拟键盘

但是我们前面已经说过,上面的原则不一定可靠,所以该种方案也未必可靠了
基于系统监控这点,我们还可以监控resize事件或者scroll事件,但是经过我的测试,setInterval表现比较好
于是,我们简单写一段代码,可靠是否满足需求:

window.alert = function (msg) {
$('body').append('<div>' + msg + '</div>')
};
function fixedWatch(el) {
if(document.activeElement.nodeName == 'INPUT'){
el.css('position', 'static');
} else {
el.css('position', 'fixed');
}
}
setInterval(function () {
fixedWatch($('#headerview header'));
}, 500);

这个问题似乎被我们修复了,但这个方案有一个致命的恶心点!不停的监控dom变化,浪费资源。
后来我又优化了一下,只有获取焦点的时候才加个定时器,代码感觉好很多了。

setTimeout(function () {
$('#dl_app img').hide();
}, 100);
window.alert = function (msg) {
$('body').append('<div>' + msg + '</div>')
};
window.res = null;
var i = 0;
function fixedWatch(el) {
alert(i++);
if(document.activeElement.nodeName == 'INPUT'){
el.css('position', 'static');
} else {
el.css('position', 'fixed');
if(window.res ) {
clearInterval(window.res );
window.res = null;
}
}
}
$('input').focus(function () {
if(!window.res) {
fixedWatch($('#headerview header'));
window.res = setInterval(function () {
fixedWatch($('#headerview header'));
}, 500);
}
});

#结束
移动端开发毕竟是个新兴行业,规范和方法都不是很健全,在这个时代,我们每一个人都是开拓者。用各式各样的方法去解决处理问题,提升自己。谨写下这个问题,愿与君共勉。
「Stay Hungry . Stay Foolish.」

Eli Pei

Eli Pei

「Stay hungry. Stay foolish.」

2 日志
1 分类
2 标签
GitHub 豆瓣 知乎 微博
© 2016 Eli Pei
由 Hexo 强力驱动
主题 - NexT.Pisces