js实现随页面滚动切换的tab栏
最近项目有个需求,比较简单,就是顶部有tab栏,点击tab栏页面滚动到对应位置,手动的滚动页面tab栏也要随之切换。项目是基于vue2+ant design vue开发的,本来是想借用组件库来方便实现的,但是ui确实相差甚远,而且想着也是个简单功能,就放弃了使用组件库,转而用js来实现这个逻辑。
实现过程中,发现这虽然只是个简单的知识点,但确实也有一些要注意的地方。
各种各样的高度、距离
我们都知道浏览器里有各种各样的高度、距离等等。实现这个功能我们也免不了需要去计算这些高度,但是除了常用的几个,把所有高度都记住确实还比较困难,在此先回顾一下这些内容。
参考文章链接:前端页面内的高度、位置简述
屏幕的宽高
screen.height:屏幕高度。
screen.width:屏幕宽度。
screen.availHeight:屏幕可用高度。即屏幕高度减去上下任务栏后的高度,可表示为软件最大化时的高度。
screen.availWidth:屏幕可用宽度。即屏幕宽度减去左右任务栏后的宽度,可表示为软件最大化时的宽度。
任务栏的宽高就可以根据上面的内容去计算出来了。
浏览器宽高
window.outerHeight:浏览器高度
window.outerWidth:浏览器宽度
window.innerHeight:浏览器内页面可用高度。此高度包含了水平滚动条的高度(若存在)。可表示为浏览器当前高度去除浏览器边框、工具条后的高度。
window.innerWidth :浏览器内页面可用宽度;此宽度包含了垂直滚动条的宽度(若存在)。可表示为浏览器当前宽度去除浏览器边框后的宽度。
当然,屏幕与浏览器的宽高可能并不常用,加下来就是一些比较常用的内容了。
body页面的宽高
body.offsetHeight:body总高度
body.offsetWidth:body总宽度
body.clientHeight:body展示的高度
body.clientWidth:body展示的宽度
所有元素的位置
有些计算是涉及到盒子模型的,在此不过多赘述
clientHeight和clientWidth:元素的内尺寸,不包含边框
offsetHeight和offsetWidth:元素的外尺寸,包含边框, 不包含外边距
offsetTop和offsetLeft:元素的左上角距离已定位的父元素左上角的距离,如果找不到这个父元素那就是body
scrollLeft和scrollTop:元素被卷起的高度和宽度
点击tab栏 -> 页面滚动
首先说明一下,我的页面中tab栏是粘性定位的,高度为80,也就是说滚动后元素距离页面顶部的高度为80就是一个合适的位置。
页面中内容并没有被已定位的父元素包裹,所以元素的offsetTop就是其距离页面顶部的距离。当然如果你的内容是被已定位元素包裹的,你可以通过 元素的offsetTop + 其定位父元素的offsetTop来计算出元素距离页面顶部的距离。
1 | // 拿到要滚动到页面顶部的内容元素 |
页面滚动的操作,我们可以借助window.scrollTo这个api来实现,可以接受一个options对象,options对象接收两个参数top和behavior,top是页面卷起的高度,behavior是滚动的行为。
如果我们没有tab栏,那么卷起 元素距页面顶部的高度,就可以使元素位于页面顶部,但是有tab栏在顶部,所以我们需要去除这个高度。
1 | window.scrollTo({ |
页面滚动 -> tab栏自动切换
此时我们可以通过点击tab栏实现页面滚动到合适的位置,反之我们手动滑动页面,tab栏也需要随之进行切换。
1 | // 监听滚动事件,使用节流的原因就不赘述了 |
兼容问题
看起来现在好像没什么问题了对吗?
但是这引发了一个新的问题,我们滚动页面是可以自动切换tab没有问题的,不过我们点击tab的时候,也会触发页面的滚动,这个页面的滚动也会触发滚动事件,这就会导致我们点击tab的时候tab不能准确的切换到指定的tab,而是跳来跳去。
这让我想到了以前遇到的的一个问题,有两个联动的组件,一个是数值的输入,一个是拖动条,两个都可以用,我们改变了一个,另一个要随之改变,也是导致了一个变动,另一个随之改变,这个随之改变又导致第一个跟随着变动。。。。。。好家伙,开始套娃了。
这次我也采取了跟上次的问题差不多的解决措施,加一个中间变量。因为我们手动对页面进行滚动是没有问题的,只有点击tab的时候才会出现这个问题,所以我们加一个中间变量,用于标识页面的滚动是否是点击tab触发的,如果是,我们就不执行滚动事件。
我用的方法比较low,就是加了个定时器
1 | // 点击tab事件 |
最开始是不想通过定时器来做的,但是查阅了关于window.scrollTo这个api,并没有提供完成之后才执行的回调函数,就拿定时器这种取巧的方法来实现了。大家也可以通过promise等方法来准确的控制哦。
这么简单的内容还啰嗦了这么就,感谢能看到这儿。
js实现随页面滚动切换的tab栏