web移动端浮层滚动阻止window窗体滚动JS/CSS处理

一、温故而知新

之前有写过在PC端“子元素scroll父元素容器不跟随滚动”的实现,原理是滚动到边缘通过preventDefault()阻止浏览器的默认行为达到我们想要的效果。

最近做移动端项目,遇到个类似的需求,就是,在众多web浏览器中,当我们出现一个浮层,浮层里面也有滚动条的时候,且有部分背景半透明的时候,就会发现,当我们滚动浮层里面的小滚动条的时候,背后整个页面都跟着一起滚走了。

后来,以为会和PC端一样,直接给<html>标签设置一个overflow:hidden声明就可以干掉窗体的滚动条,结果发现自己有些天真了。

后来经过一些研究以及测试,得到了下面这种双管齐下的处理方法。

二、阻止移动端窗体滚动的JS/CSS处理

首先CSS层面,在<html>标签上增加一个类名,例如noscroll,然后配合如下CSS和JS代码:

.noscroll,
.noscroll body {
    overflow: hidden;
}
.noscroll body {
    position: relative;
}

然后,当浮层出现的时候:

$('html').addClass('noscroll');

当浮层隐藏的时候:

$('html').removeClass('noscroll');

可以让一部分浏览器的窗体不能滚动,但不包括Safari等浏览器,怎么办呢?

我们可以在浮层touchmove的时候,阻止默认事件达到避免滚动的问题,例如:

$('aside').on('touchmove', function(event) {
    event.preventDefault();
});

这种处理兼容性强,效果最好,但是有一个问题,就是如果浮层自己也有滚动,那么这种处理会让浮层里面自己的滚动行为也无法触发,因此,我们的处理要更进一步,如下:

  1. 当手指touchstart的元素不是滚动容器同时不失容器的子元素的时候,阻止默认行为,;
  2. 如果手指touchstart的元素是滚动容器或者容器子元素的时候,不阻止默认行为,但不包括滚动到容器边缘的时候。

根据上述原理,我自己抽象了一个简单的方法,方法名和语法如下,完整代码见这里

$.smartScroll(container, selectorScrollable);

依赖Zepto.js或者jQuery.js,其中:
container表示委托的浮层容器元素($包装器对象),或者页面其他比较祖先的元素,但是,非常不建议使用$(document)或者$(document.body)等对象作为委托容器,因为可能会出现类似下面这样的错误提示:

Unable to preventDefault inside passive event listener due to target being treated as passive.

浏览器可能会把这些窗体元素的组织默认行为认为是不可取的消极的,会不被支持。

selectorScrollable表示container中可以滚动的元素的选择器,表示真正的滚动的主体。

$.smartScroll()方法的完整代码可以参见演示demo,您可以狠狠地点击这里:浮层元素滚动窗体不滚动的CSS/JS处理demo

或者扫码访问:
演示文档的二维码地址

综合了上面的CSS noscroll处理和JS的$.smartScroll()方法处理。

demo页面有如下图所示的两个按钮:
阻止滚动按钮示意

其中,上面的按钮,仅仅是采用了CSS限制,会发现,手指移动左侧半透明边缘的时候,例如在iPhone的Safari浏览器下,后面的页面内容也被滚走了;但是,一旦点击下面的测试按钮,会发现,此时,仅仅浮层里面的容器可以滚动,一旦到了边缘或者其他任何地方,都不会引起后面页面内容的滚动。

移动半透明背景色与移动

这就是我们要实现的,浮层元素自己可以滚动,窗体不能滚动的效果。

更新于2017-01-12
这样的,此方法可以让95%+的场景都是正常。根据自己工作人员测试,有部分Android还会有微移的情况,可以试试边缘判断更钝一点。不过,最终我们并没有追求100%设备完全完美,这是成本和场景后权衡之后的一种策略。

三、2016年的最后一条结束语

12月份是个超级忙碌的1个月份,只能趁着元旦周末2016年的最后1天再写点什么。

2016年依然是非常忙碌奋斗的一年,每天都在学习与进步。不过而大多数人不一样,学习的重心依然是CSS基础知识,事无巨细和深入到底两个方向,其实基本上都没什么实用价值的东西,兴趣所在而已。并没有过多关注上层建筑,一些流行事物也就看看而已;没有参加任何论坛会议之类,没有任何的社交和抛头露面,坚持分享和知识传播,无论是公司内还是对外;也几乎没有花精力在开源项目上,无论是自己的还是协助贡献。以保证有限的精力能够专注在1~2件事情上。依旧奋斗在项目一线,尽量避免面试,人员管理这些琐碎的事情,目前对当领导不感兴趣,我只是个打杂的,所以,小伙伴写邮件提问与交流的时候,不要叫我领导,我不是;也不要叫我大神,更不是。

虽然工作快7年,但是还是认为自己在积累阶段,希望在2017年可以慢慢开点花结点果。可能因为钓鱼的缘故,我是个非常耐得住性子的人,不急功近利,不心浮气躁,保持自由,不被外在环境束缚,按照自己的目标和规划走。

就这些吧~

祝大家2017年升职加薪,幸福美满!

(本篇完)

分享到:

标签: , , ,

赞助商广告(我也要赞助)



发表评论(目前27 条评论)

  1. magicbing说道:

    有没有小程序里的解决方案啊,没有document真是难搞

  2. 一只逗逼龙说道:

    感谢旭哥给予的解决方案,由于我们公司长年都会有大量的移动端活动页面,这个问题也困扰了我们很久,我们尝试过多种方案,但是之前的方案存在各种小缺陷。现在打算尝试采用您这种方案,如果覆盖面能达95%那就是巨牛掰的存在了。后续会通过大量实验给您反馈结果的

  3. imgream说道:

    selectorScrollable的左右滑动也被阻止了,比如有个分享框 里面的按钮是左右滑动的,用了$.smartScroll()方法就不能左右滑了。能否再优化下,实现container不可滚动,但selectorScrollable可以左右滑动。

    • 张 鑫旭说道:

      @imgream 如果同时上下左右都可以滑动那就啰嗦了,所以,可能并不会支持,成本和收益略低,不想把小工具弄得太复杂~

  4. 珠宏说道:

    我看到了isSBBrowser

  5. ziven27说道:

    张老师这个问题我之前的解决方案是(但结构不是父子关系),
    你的结构:html>body>{content}+#modal
    我的结构:html>body>(#page>{content})+#modal
    这样兄弟的关系的话就避免你的那个问题了,但是用起来给用户的感觉其实还是嵌套的,所以我觉得是个备选方案。
    还有一个点就是postitiion:fixed在iso上总是会出现很多不友好的地方,比如输入法弹出来的时候网页容易被输入法挡住。用position:absolute;可能会不需要考虑那么多的兼容性问题。
    以上/

  6. ziven27说道:

    说个题外话:
    不知道张老师是不是看过“血战钢锯岭“这个电影,感觉你和男主都有着自己坚持的东西。

  7. Howard说道:

    在body加了position:fixed可以解决您的问题吗?

  8. 旧时长安说道:

    之前项目上也遇到这样的问题,有两种解决方案,一个是禁止滚动,用touchmove模拟滚动,缺点是不够平滑;第二个是用控件iscroll解决; 现在又多了一种解决方案,谢谢啦,2017,我们一起进步~

  9. king小龙说道:

    现在正在学习,很好奇公司内部对于开发时前端的工作流程能否给予一下具体的解答谢谢大神!

  10. 木铎骑黄牛说道:

    这个插件在魅族pro6 (系统版本android 6.0)的微信浏览器中(微信版本6.5.3)失效,滑动时,背景仍会滑动。

  11. 二少说道:

    scrollIntoView 的定位可以添加平滑过渡的效果嘛,试了好久没成功

  12. catalsdevelop说道:

    坚果手机,更新到了最新的系统,自带的浏览器,最后那个例子,两个按钮弹出来的滚动,还是会影响外层。。。

  13. pt涛说道:

    旭哥,你上面的东西很实用,希望你多多更新!祝工作愉快,事业顺心。。

  14. yukap说道:

    精力专注到1-2件事情上,说的太棒了。给结束语点赞

  15. karen说道:

    旭哥做的这个我用手机试了一下效果 ,第二种方法在只滑动一次的时候,底下的页面是不会移动的,但是在反复多次滑动之后,下面的页面会出现滑动的情况,这是什么原因呢???

  16. xfan说道:

    感谢这些基础css文章,它们真的很有用~~

  17. 最爱的是不二说道:

    看到最后一段要泪目了,我无法像你这么耐得住性子,一直大火的ES6、vue、node…等等,我也想去学习,可是更新太快,而我学习的速度远远更不上这些知识更新的速度,但我有无法耐心去专研css的内容,在公司只是css比较厉害好像无法站住脚。我也不知道改怎么办,好难受….新年快乐

  18. 盖大楼说道:

    …吆,你居然可以看见我,只有一种可能,我被你拐卖到外地了!
    真巧工作中遇到一个问题,要写了一个整屏滚动的东西。一开始第一页往下滑第二页,往上滑第一页。 但是设计给的图太长,要是手机短或者手机浏览器不是全屏模式,只能看到部分内容。就想着内部的能滑动 ,这样不管屏幕短还是长,都可以看到全部内容。再滑动的时候 切换页。但是QQ浏览器 向上滑没问题,向下滑会出现QQ的自带下拉框。无法向上换页。这个demo正好可以阻止 QQ浏览器自带的下拉框。网上找了很久都没有这种的!
    非常感谢!新年快乐!

  19. 奥丁海兹说道:

    新年快乐

  20. 黑暗骑士说道:

    新年快乐 你的博客解决了我很多问题 过年了 在这里 谢谢你的博客

  21. GIN说道:

    顶一个

  22. Snake说道:

    看到结尾想着说上一句,能够这样坚持和耐得住性子真心不简单,有时候自己看着身边的同事走向管理路线感觉心理痒痒

  23. n说道:

    见过一个纯 css 的方案 http://www.luxiyalu.com/playground/overlay/
    不过很少看到其他人提起……