在移动端 WEB 开发的时候(小程序也雷同),如上录屏所示,如果页面超过一屏高度出现滚动条时,在 fixed 定位的弹窗遮罩层上进行滑动,它下面的内容也会跟着一起滚动,看起来好像事件穿透到下面的dom元素上一样,我们姑且称之为滚动穿透。
能够猜想是文档(document)的滚动事件被触发了,如果能禁用滚动事件就好办了。
<div class="btn">点击出现弹窗</div>
<div class="popup">
<div class="popup-mask"></div>
<div class="popup-body popup-bottom">
<div class="header">我是标题</div>
<div class="content">
<div>0</div>
<div>1</div>
<div>...</div>
</div>
</div>
</div>
.popup-mask {
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
z-index: 998;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.popup-body {
padding: 0 50px 40px;
background-color: #fff;
position: fixed;
z-index: 999;
}
默认情况下,平移(滚动)和缩放手势由浏览器专门处理,但是可以通过 css 特性 touch-action 来改变触摸手势的行为。摘取几个 touch-action 的值如下。
值 | 描述 |
---|---|
auto | 启用浏览器处理所有平移和缩放手势。 |
none | 禁用浏览器处理所有平移和缩放手势。 |
manipulation | 启用平移和缩放手势,但禁用其他非标准手势,例如双击缩放。 |
pinch-zoom | 启用页面的多指平移和缩放。 |
于是在 popup 元素上设置该属性,禁用元素(及其不可滚动的后代)上的所有手势就可以解决该问题了。
.popup {
touch-action: none;
}
Note: [无障碍设计] 阻止页面缩放可能会影响视力不佳的人阅读和理解页面内容,不过小程序本身好像就不可以缩放!
来自 W3C 的一个标准。大意是说,在 touchstart 和 touchmove 事件中调用 preventDefault 方法可以阻止任何关联事件的默认行为,包括鼠标事件和滚动。
因此我们可以这样处理。
Step 1、监听弹窗最外层元素(popup)的 touchmove 事件并阻止默认行为来禁用所有滚动(包括弹窗内部的滚动元素)。
Step 2、释放弹窗内的滚动元素,允许其滚动:同样监听 touchmove 事件,但是阻止该滚动元素的冒泡行为(stopPropagation),使得在滚动的时候最外层元素(popup)无法接收到 touchmove 事件。
const popup = document.querySelector('.popup')
const scrollBox = document.querySelector('.content')
popup.addEventListener('touchmove', (e) => {
// Step 1: 阻止默认事件
e.preventDefault()
})
scrollBox.addEventListener('touchmove', (e) => {
// Step 2: 阻止冒泡
e.stopPropagation()
})
如上录屏所示,弹窗内也含有滚动元素,在滚动元素滚到底部或顶部时,再往下或往上滚动,也会触发页面的滚动,这种现象称之为滚动链(scroll chaining), 但是感觉滚动溢出(overscroll)这个名字更言辞达意。
overscroll-behavior 是 CSS 的一个特性,允许控制浏览器滚动到边界的表现,它有如下几个值。
值 | 描述 |
---|---|
auto | 默认效果,元素的滚动可以传播到祖先元素。 |
contain | 阻止滚动链,滚动不会传播到祖先元素,但是会显示节点自身的局部效果。例如 Android 上过度滚动的发光效果或 iOS 上的橡皮筋效果。 |
none | 与 contain 相同,但是会阻止自身的过度效果。 |
所以可以这样解决问题:
.content {
overscroll-behavior: none;
}
简洁干净高性能,不过 Safari 全系不支持,兼容性如下,有没有感觉 Safari 就是现代版的 IE(偶然听路人说的)!
借用 event.preventDefault 的能力,当组件滚动到底部或顶部时,通过调用 event.preventDefault 阻止所有滚动,从而页面滚动也不会触发了,而在滚动之间则不做处理。
let initialPageY = 0
scrollBox.addEventListener('touchstart', (e) => {
initialPageY = e.changedTouches[0].pageY
})
scrollBox.addEventListener('touchmove', (e) => {
const deltaY = e.changedTouches[0].pageY - initialPageY
// 禁止向上滚动溢出
if (e.cancelable && deltaY > 0 && scrollBox.scrollTop <= 0) {
e.preventDefault()
}
// 禁止向下滚动溢出
if (
e.cancelable &&
deltaY < 0 &&
scrollBox.scrollTop + scrollBox.clientHeight >= scrollBox.scrollHeight
) {
e.preventDefault()
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>滚动穿透与滚动溢出</title>
<style>
body {
padding: 60px;
height: 150vh;
}
.btn {
display: inline-block;
background-color: red;
color: #fff;
border-radius: 8px;
padding: 10px 20px;
margin-bottom: 30px;
}
.popup {
display: none;
/* touch-action: none; */
}
.popup-mask {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 998;
}
.popup-body {
position: fixed;
z-index: 999;
padding: 0 50px 40px;
background-color: #fff;
}
.popup-bottom {
left: 0;
right: 0;
bottom: 0;
}
.header {
font-size: 18px;
text-align: center;
line-height: 3;
background-color: blanchedalmond;
}
.content {
max-height: 40vh;
background-color: greenyellow;
overflow: auto;
/* overscroll-behavior: none; */
}
</style>
</head>
<body>
<div class="btn">点击出现弹窗</div>
<div class="page-content">这个页面很高哦</div>
<div class="popup">
<div class="popup-mask"></div>
<div class="popup-body popup-bottom">
<div class="header">我是标题</div>
<div class="content"></div>
</div>
</div>
<script>
const pageContent = document.querySelector('.page-content')
const scrollBox = document.querySelector('.content')
const btn = document.querySelector('.btn')
const popup = document.querySelector('.popup')
const mask = document.querySelector('.popup-mask')
for (let i = 0; i < 30; i++) {
const child = document.createElement('div')
child.textContent = '这个页面很高哦'
pageContent.appendChild(child)
}
for (let i = 0; i < 30; i++) {
const child = document.createElement('div')
child.textContent = i
scrollBox.appendChild(child)
}
btn.addEventListener('click', () => {
popup.style.display = 'block'
})
mask.addEventListener('click', () => {
popup.style.display = 'none'
})
/**
* 滚动穿透
*/
popup.addEventListener('touchmove', (e) => {
e.preventDefault()
})
scrollBox.addEventListener('touchmove', (e) => {
e.stopPropagation()
})
/**
* 滚动溢出
*/
let initialPageY = 0
scrollBox.addEventListener('touchstart', (e) => {
initialPageY = e.changedTouches[0].pageY
})
scrollBox.addEventListener('touchmove', (e) => {
const deltaY = e.changedTouches[0].pageY - initialPageY
// 禁止向上滚动溢出
if (e.cancelable && deltaY > 0 && scrollBox.scrollTop <= 0) {
e.preventDefault()
}
// 禁止向下滚动溢出
if (
e.cancelable &&
deltaY < 0 &&
scrollBox.scrollTop + scrollBox.clientHeight >= scrollBox.scrollHeight
) {
e.preventDefault()
}
})
</script>
</body>
</html>
BetterScroll 是一款重点解决移动端各种滚动场景需求的开源插件,有下列功能支持滚动列表,下拉刷新,上拉刷新,轮播图,slider等功能。better-scroll通过使用惯性滚动、边界回弹、滚动条淡入淡出来确保滚动的流畅。
基于webkit内容隐藏滚动条,核心代码是:overflow的值需要设置为auto或者scroll,然后设置::-webkit-scrollbar的display属性为none。当然也可以通过设置滚动条的宽度为0。
原生js获取滚动条在Y轴上的滚动距离、获取文档的总高度、获取浏览器视口的高度的方法实现,用于判断页面加载数据。
iScroll 是一款针对web app使用的滚动控件,它可以模拟原生IOS应用里的滚动列表操作,还可以实现缩放、拉动刷新、精确捕捉元素、自定义滚动条等功能。
把原有的滚动条隐藏,创建个新的滚动条,并拓展其宽度,达到隐藏的效果,而判断滚动条是否滚动,保存原有的滚动条到顶部的距离,看是否发生改变,做出相应的判断。
window.onscroll为滚轮监听,document.body.scrollTop||document.documentElement.scrollTop 写两个是为了兼容不同浏览器。固定位置的top要设为负值,原因不明,若为0则会跟上方有空隙。左右位置虽然是0也要设,不然若为不是100%宽度的内容会出现左右跳动。
这篇文章主要介绍使用js实现文字无间歇性上下滚动,一些网站的公告,新闻列表使用的比较多,感兴趣的小伙伴们可以参考一下
在前端开发中,需要获取浏览器滚动距离的需求,这篇文章主要讲解如何使用原生Js兼容实现获取浏览器获X轴,Y轴的滚动距离。并延伸扩展下我们一些不知道的js知识,希望对你有所帮助。
当页面特别长的时候,用户想回到页面顶部,必须得滚动好几次滚动键才能回到顶部,如果在页面右下角有个返回顶部的按钮,用户点击一下,就可以回到顶部,对于用户来说,是一个比较好的体验。
我们都知道,撸页面的时候当我们的内容超出了我们的div,往往会出现滚动条,影响美观。百度下大部分都是在说overflow:hidden或者overflow-y: no可以解决问题,但是并不能很好的解决我们的问题,那么怎么办呢?
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!