滑动穿透(锁body)终极探索

场景

当页面出现浮层的时候,滑动浮层的内容,正常状况下预期应该是浮层下边的内容不会滚动;然而事实并不是如此。 css

如图所示,浮层下边的内容并无如想象中不受影响。

解决

先去github上搜索一番,发现有解决此问题的开源包,简单粗暴直接挑选了其中star的最高的(body-scroll-lock)操做一番! 使用后发现有一些问题:html

  • 安卓端全挂
  • ios端偶尔会有锁不住的状况

查源码发现该包在iOS端使用禁止touchmove的方式单独处理,可是在其余端只是给bodyoverflow: hidden简单处理。 因而决定写一个针对多端通用的包来处理相似的问题。android

探索一:overflow: hidden

看到下边的滚动确定马上就想到了是整个viewport的滚动,那么若是给body设置overflow: hidden,此时body的内容就只有一屏了,确定不会滚动了;ios

body { overflow: hidden; }
复制代码复制代码

此方案在pc端完美解决了咱们的问题,然而事情并无那么简单;git

再试试移动端: github

移动端中并无出现期待的效果。。。 既然pc端已经有了完美的方案,下边咱们继续探索移动端的解决方案。

探索二:body定位

上边想到给body设置overflow: hidden在移动端并不能解决咱们的问题,是否在于body的height没有设置web

  • 将html、body的高度都设置为100%
  • 给body设置绝对定位(fixed);

同时使用这两个操做彷佛完美知足了咱们的需求; chrome

可是如图所示,每次都会将页面拉到最顶上的位置,这样看起来又不完美了; 既然使用了定位,那么给一个 top值不就定位到咱们想要的位置了(聪明如我)。

tips: body 设置 relative 定位会页面自身拉上去,下边留白npm

屡次实验发现这个方案在android端中完美达到了咱们想要的结果,可是在ios端并不理想;每次定位的时候会有闪动的问题;好事多磨,接着探索ios端的方案。浏览器

探索三:禁止touchmove

若是禁止掉页面的touchmove是否可行呢?话很少说就是干! 当弹出浮层的时候禁掉页面元素的touchmove

document.addEventListener('touchmove', function (event) {
    event.preventDefault()
})
复制代码复制代码

测试发现没有达到想象中的效果,感受这个结果并不能接受啊,禁止documenttouchmove都不能禁止滚动的吗?

进一步的探索后发现缘由竟是这个属性

  • passive addEventListener第三个参数中传入

原来是浏览器作的一些优化,chrome passive-event-listeners Passive Event Listeners是Chrome提出的一个新的浏览器特性:Web开发者经过一个新的属性passive来告诉浏览器,当前页面内注册的事件监听器内部是否会调用preventDefault函数来阻止事件的默认行为,以便浏览器根据这个信息更好地作出决策来优化页面性能。当属性passive的值为true的时候,表明该监听器内部不会调用preventDefault函数来阻止默认滑动行为,Chrome浏览器称这类型的监听器为被动(passive)监听器。

知道问题就好说了,给addEventListener传入第三个参数

document.addEventListener('touchmove', function (event) {
    event.preventDefault()
}, { passive: false })
复制代码复制代码

大功告成! 忽然想到,若是浮层中还须要滚动那就不GG了! so,是否是能够有选择性的禁止滚动(在浮层中元素滚动到最顶部或者最底部以后禁止滚动)。 单独处理浮层中须要滚动的元素;

targetElement.ontouchmove = function (event) { const clientY = event.targetTouches[0].clientY - initialClientY
<span class="hljs-keyword">if</span> (targetElement &amp;&amp; targetElement.scrollTop === <span class="hljs-number">0</span> &amp;&amp; clientY &gt; <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">return</span> preventDefault(event)
}

<span class="hljs-keyword">if</span> (targetElement &amp;&amp; (targetElement.scrollHeight - <span class="hljs-number">1</span> - targetElement.scrollTop &lt;= targetElement.clientHeight) &amp;&amp; clientY &lt; <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">return</span> preventDefault(event)
}

event.stopPropagation()
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
复制代码
复制代码<span class="hljs-keyword">if</span> (targetElement &amp;&amp; targetElement.scrollTop === <span class="hljs-number">0</span> &amp;&amp; clientY &gt; <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> preventDefault(event) } <span class="hljs-keyword">if</span> (targetElement &amp;&amp; (targetElement.scrollHeight - <span class="hljs-number">1</span> - targetElement.scrollTop &lt;= targetElement.clientHeight) &amp;&amp; clientY &lt; <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> preventDefault(event) } event.stopPropagation() <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span> 复制代码} 复制代码复制代码

这个方案在ios中完美实现,可是在 android中仍是有一点问题;浮层内容拉到最顶部或者最底部的时候依然会带动页面的内容有必定程度的移动。

tua-body-scroll-lock

终极方案来啦!

tua-body-scroll-lock便是在iosandroidPC各个端单独处理,保证在每一个端均可以实现完美的效果!

demo

安装

$ npm i -S tua-body-scroll-lock
# OR
$ yarn add tua-body-scroll-lock
复制代码复制代码

使用

移动端

import { lock, unlock } from 'tua-body-scroll-lock'

// 禁止滑动后还须要内部能够滚动的元素(针对移动端ios处理) const targetElement = document.querySelector("#someElementId");

复制代码lock(targetElement) unlock(targetElement) 复制代码复制代码
PC端

tips: PC端不须要targetElement, 不传targetElement也不想要控制台提示能够传null

import { lock, unlock } from 'tua-body-scroll-lock' 复制代码lock() unlock() 复制代码复制代码

转自滑动穿透(锁body)终极探索

相关文章
相关标签/搜索