注:如下全部例子均 只 在 iOS 的微信中测试过,但对于饿了么APP的内置浏览器一样适用(二者使用相同内核)css
工做中经常有须要显示大量信息的状况,列表超出一屏就涉及到滚动的问题。例如html
- var n = 1 ul while n <= 100 li= n++
在 iOS 中用微信打开,滚动很是顺滑,so far so good!但某天产品需求有变,要求加一个固定在头部的标题,因而改为这样:前端
- var n = 1 h1= "Momentum Scrolling on iOS" ul while n <= 100 li= n++
body, ul { margin: 0; } html, body { height: 100%; } body { display: flex; flex-direction: column; } h1 { flex-shrink: 0; } ul { flex: 1; overflow: scroll; }
直接用 flex 盒模型实现,动态适应标题的高度,很简单不是么。可是这时在 iOS 上打开后测试,发现有问题,下半部分区域滚动起来感受很不顺滑,用老板的话说就像“卡齿轮”ios
这时就有大牛推荐了传说中的神器:-webkit-overflow-scrolling: touch
web
ul { flex: 1; overflow: scroll; -webkit-overflow-scrolling: touch; }
很简单的一个属性,顺滑滚动效果就回来了!虽然不太明白是怎么回事,解决问题就好。 可是产品经理又说了,须要在滚动时获取滚动条的位置作些其余操做。太简单了,加个 scroll
事件搞定。segmentfault
document.querySelector('ul').addEventListener('scroll', function() { this.previousElementSibling.textContent = 'ScrollTop: ' + this.scrollTop; })
随手写好在浏览器中测试经过,然而在手机上测试就不太对劲:那个值是会变,然而滚动的时候不变,只有在滚动结束后变一次。浏览器
整个滚动过程当中 scroll 事件只在滚动结束后会被触发一次,问题是出在这个所谓的神器 -webkit-overflow-scrolling
上面安全
一个只有 iOS 设备支持的非标准属性。苹果本身的解释:指定是否在 overflow: scroll
的元素中使用“原生”的滚动方式微信
他包含两个可选值:auto
和 touch
app
什么是 stacking context
?这能够说是CSS里一个阴暗面,极其晦涩。有兴趣的朋友能够去看高人的解释,这里不作讨论(其实笔者本身也不是很是明白:cry:),总之全部的坑都是由此而起。
下面列出我遇到过的坑:
严格来讲,上面的 scroll 事件不触发只是本坑的一个反作用,因此说没必要考虑经过 touchmove
事件转发 scroll
事件等点子,scroll
事件触发了同样检测不到 scrollTop
属性的变化(固然检测手指的移动距离另说)。一样,检测滚动区域内部元素的 getBoundingClientRect
一样无效。
例中起了一个无限的rAF循环不停地获取 scrollTop
的值,然并卵。
这个更奇葩。例中用一个半透明的 div
盖在了滚动区域 ul
上面(实践中多是一个弹框的背部蒙版),甚至给 ul
本身加上了 pointer-events: none
,手指在 div
上滑动仍然会触发 ul
的滚动。你能够在显示半透明蒙版时将 ul
的 -webkit-overflow-scrolling: touch
或 overflow: scroll
去掉,可是会形成屏幕明显的闪烁。若是给 body
的 touchmove
事件 preventDefault()
能够防止触发滚动,可是是全部滚动区域都会失效。
Google 上一搜一片,可是笔者没有遇到过,或许在新版本系统中已经修正,这里不展开讨论。
transition
……
WKWebView
替换 UIWebView
内核可能有些读者已经发现,scroll
事件不能触发的坑在 iOS Safari 和 iOS Chrome 浏览器中不存在,为何呢?这里要从 iOS 上浏览器的发展史提及。
因为苹果公司对安全性等缘由的考虑,苹果公司静止第三方浏览器在 iOS 设备上使用本身的浏览器的内核,换句话说,使用本身内核的浏览器都被禁止上架 AppStore。各大厂商无奈,因而长久以来,包括 Chrome
在内的全部第三方浏览器,都只是使用 iOS 系统内置的浏览器控件包一层外壳,这个控件就是 UIWebView
。这个 UIWebView
不只速度差,HTML5 支持率低,占用内存高,还有各类各样奇怪的问题。然而苹果公司却给本身的 Safari 浏览器开了后门。首先 Safari 使用的支持 JIT 编译的 JS 引擎内核 Nitro
比 UIWebView
里老旧的解释性 JavaScriptCore
内核速度搞数倍,而后 HTML5 支持度也比 UIWebView
高,还少了某些奇葩bug。长此以往就造成了 iOS 设备上 Safari 浏览器全面碾压其余第三方浏览器的现象。
在乔帮主撒手人寰不久以后,苹果公司口气终于松动,虽然没有放开第三方浏览器内核的限制,但把 Safari
的浏览器内核提取了出来开放第三方浏览器使用,那就是现在的 WKWebView
(WK 即 Webkit 的缩写)。但因为 WKWebView
只支持 iOS8 以上系统,各大浏览器厂商并未马上跟进。直到最近的 iOS9 时代,Chrome 成为第一个吃螃蟹的 APP,使用了 WKWebView
内核。测试数据代表,使用 WKWebView
内核的 Chrome 浏览器在速度和 HTML5 支持率上已经与 Safari 浏览器不相上下。紧接着 Mozilla 公司宣布 Firefox 登陆 iOS 平台,使用的也是 WKWebView
内核(因而有了第一款基于 Webkit
内核的火狐浏览器 :)
不知苹果作了什么手脚,也许苹果的开发人员认为 WKWebView
的效能已经足以支撑在 scroll
事件中执行额外代码而不形成 UI 卡顿,总之在 WKWebView
内核中滚动能够正常触发 scroll
事件,固然也能正常得到 scrollTop
的值。然而通过测试第二个问题仍然存在。
在这里笔者强烈建议各个 APP 迁移内嵌浏览器至新的 WKWebView
内核。可是就我看到的,包括微信和饿了么在内,几乎全部的国产 APP 都还在使用 UIWebView
内核,这不得不说是一大前端开发之殇。
好比前段时间很火的 iScroll,笔者曾近也使用过一段时间。最后得出的结论是:iScroll 挖出的坑不比它填上的坑少,好比在 iScroll 里加个 click
事件都要当心翼翼、特别对待(由于绝大多数状况绑定 touch
事件的回调函数里第一件作的事情就是 preventDefault
)。
最值得一提的是 iScroll 的速度问题,比原生实在相差太多,在中低端安卓机型上卡顿明显,若是还要绑定 scroll
事件作些别的事情就更卡了。
总之笔者并不建议使用。
也许这才是真正的终极解决之道